为什么cat命令不使用它的命令环境?

为什么cat命令不使用它的命令环境?

AFAIK,cat是一个外部命令,它在执行时分叉一个新进程,就像sh -c执行脚本一样。话虽如此,我希望cat使用它的命令环境,因为它被其他外部命令使用,例如

f=test.txt sh -c 'cat "$f"'

这不应该显示文件的内容吗

f=test.txt cat $f    


注意:我不是在问什么是 someVariable=someValue command.我问为什么第一个示例使用其命令变量而不是第二个示例。变量扩展在第二个示例中发生的方式也应该在第一个示例中发生。

答案1

概括

该命令f=test.txt sh -c 'cat "$f"'会产生输出,因为变量赋值f=test.txt发生在(单引号)命令参数的扩展之前'cat "$f"'。单引号可防止在cat "$f"执行子命令之前发生扩展。

该命令f=test.txt cat $f确实不是f=test.txt因为发生变量赋值而产生输出(未引用的)命令参数的扩展$f

为什么f=test.txt cat $f不产生任何输出

首先,我将尝试解释为什么该命令f=test.txt cat $f没有产生任何输出,尽管您期望它会产生任何输出。这里对于所谓的评估顺序可能有些轻微的混淆简单的命令

您可能假设命令序言中的变量赋值(即赋值f=test.txt)发生在命令主体中的变量扩展(即$fin的扩展cat $f)之前。但事实并非如此。为了验证这一点,我们可以参考页面简单的命令扩展在 Bash 手册中或关于简单命令的小节在里面POSIX规范。这两篇参考文献都包含以下段落:

“简单命令”是一系列可选的变量分配和重定向,可以任意顺序,可选地后跟单词和重定向,并由控制运算符终止。

当需要执行给定的简单命令时(即,当任何条件结构,例如 AND-OR 列表或案件语句没有绕过简单命令),以下扩展、赋值和重定向都应从命令文本的开头到结尾执行:

  1. 根据以下规则被识别为变量赋值或重定向的单词Shell 语法规则保存以供步骤 3 和 4 中的处理。

  2. 非变量赋值或重定向的词应扩展。如果扩展后仍有任何字段,则第一个字段应被视为命令名称,其余字段是命令的参数。

  3. 重定向应按中所述执行重定向

  4. 每个变量赋值都应在赋值之前进行扩展,以进行波形符扩展、参数扩展、命令替换、算术扩展和引号删除。

请注意,步骤 2 是命令中发生变量扩展的地方,但步骤 1 告诉我们变量赋值将保存到步骤 3 和 4。因此,表达式在赋值发生之前cat $f扩展为(不带参数)。这解释了为什么您没有得到任何输出。catf=test.txt

有关此主题的进一步讨论,请参阅以下帖子:

为什么f=test.txt sh -c 'cat "$f"' 产生输出

接下来我将尝试解释为什么该命令f=test.txt sh -c 'cat "$f"' 产生输出。为此,我们要看看shell 执行的常规操作的完整列表:

  1. shell 从文件中读取输入(参见),来自 -c 选项或来自系统()波彭()POSIX.1-2008 系统接口卷中定义的函数。如果 shell 命令文件的第一行以字符“#!”开头,则结果未指定。

  2. shell 将输入分解为标记:单词和运算符;看令牌识别

  3. shell 将输入解析为简单的命令(请参阅简单命令)和复合命令(参见复合命令)。

  4. shell 对每个命令的不同部分(分别)执行各种扩展,从而产生被视为命令和参数的路径名和字段列表;看词表达式

  5. shell 执行重定向(参见重定向) 并从参数列表中删除重定向运算符及其操作数。

  6. shell 执行一个函数(参见功能 定义 命令),内置(参见特殊的内置实用程序)、可执行文件或脚本,将参数名称指定为编号为 1 到 n 的位置参数,并将命令名称(或者在脚本内的函数的情况下,指定脚本的名称)作为位置参数编号为 0(参见命令搜索和执行)。

  7. shell 可选择等待命令完成并收集退出状态(请参阅命令的退出状态)。

因此,您可以在此处看到调用函数/内置/可执行文件/脚本(此列表中的第 6 步)解析简单命令。因此f=test.txt发生分配程序执行sh -c 'cat "$f"'。因为参数是单引号的,所以只会被解析命令执行。因此子命令扩展为cat "test.txt".

答案2

shell 命令上的变量分配仅影响传递给该命令的环境,而不影响执行 shell 的环境。

您可以更直接地看到这一点

$ f=1
$ f=2 echo $f
1
$

在设置为 的echo环境中运行,但分配在(正在构建的)命令的环境中运行,而不是在用于构建命令的 shell 环境中运行。f2

只有不带命令的赋值才会影响正在运行的 shell 环境,并且 shell 环境用于构造命令。

答案3

因为在您的第二个示例中,$f是由您的 shell 扩展的,而不是由cat.假设f在您的 shell 环境中没有进行其他设置,$f则扩展为空字符串,cat然后从 stdin 读取,就像没有给定参数一样。

在你的第一个例子中,$f位于单引号内,因此它不会被原始 shell 扩展,但是通过实例扩展sh看到cat "$f" f在其环境中设置。

相关内容