为什么在 bash 中在命令之前设置变量是合法的?

为什么在 bash 中在命令之前设置变量是合法的?

我刚刚遇到了几个答案,例如正在解析分隔文本文件...使用以下结构:

while IFS=, read xx yy zz;do
    echo $xx $yy $zz
done < input_file

其中IFS变量在命令之前设置read

我一直在阅读bash参考但不明白为什么这是合法的。

我试过

$ x="once upon" y="a time" echo $x $y

从 bash 命令提示符但没有得到任何回显。有人可以指出我在参考中定义该语法的位置,允许以这种方式设置 IFS 变量吗?这是特殊情况还是我可以用其他变量做类似的事情?

答案1

相关信息可以在手册页由 BASH 维护者提供(最后一次检查于 2020 年 8 月)。 Shell 语法部分,简单命令指出(添加了强调):

一个简单的命令是可选变量赋值的序列 后跟空格分隔 单词和重定向,并由控制操作符终止。这第一个字指定要执行的命令,并作为参数零传递。其余单词作为参数传递给调用的命令。

所以你可以传递任何你想要的变量。您的echo示例不起作用,因为变量被传递给命令,而不是在 shell 中设置。外壳膨胀$x$y 调用命令。这有效,例如:

$ x="once upon" y="a time" bash -c 'echo $x $y'
once upon a time

答案2

定义的变量就像分叉进程上的环境变量一样。

如果你跑

A="b" echo $A

然后 bash 首先扩展$A""然后运行

A="b" echo

这是正确的方法:

x="once upon" y="a time" bash -c 'echo $x $y'

注意 中的单引号bash -c,否则你会遇到与上面相同的问题。

因此,您的循环示例是合法的,因为 bash 内置“read”命令将在其环境变量中查找 IFS,并找到,.所以,

for i in `TEST=test bash -c 'echo $TEST'`
do
  echo "TEST is $TEST and I is $i"
done

将打印TEST is and I is test

最后,就语法而言,在 for 循环中需要一个字符串。因此我不得不使用反引号将其变成命令。但是,while 循环需要命令语法,例如IFS=, read xx yy zz.

答案3

man bash

环境

[...] 任何简单命令或函数的环境都可以通过在其前面添加参数分配来临时增强,如上面的参数中所述。这些赋值语句仅影响该命令所看到的环境。

变量在变量赋值之前展开。出于显而易见的原因var=x,反之亦然,但var=$othervar事实并非如此。即,$x在可用之前需要您的。但这不是主要问题。主要问题是命令行只能由 shell 环境修改,但赋值不会成为 shell 环境的一部分。

您混淆了功能:您想要替换命令行,但将变量定义放入命令环境中。命令行替换必须由 shell 进行。所调用的命令必须显式使用该环境。是否以及如何完成此操作取决于命令。

这种用法的好处是可以为子进程设置环境而不影响shell环境。

x="once upon" y="a time" bash -c 'echo $x $y'

正如您所期望的那样,因为在这种情况下,这两个功能被组合在一起:命令行替换不是由调用 shell 完成的,而是由子进程 shell 完成的。

答案4

我要追求更大的图景“为什么这是合法的”

答案:这样您就可以调用或调用程序,并且对于该调用,仅使用具有特定变量的变量。

举个例子:您有一个名为“db_connection”的数据库连接参数,通常您传入“test”作为测试数据库连接的名称。事实上,您甚至可以将其设置为默认值,然后不需要显式传递。然而有时您想使用 ci 数据库。因此,您将参数作为“ci”传递,然后被调用的程序使用数据库参数作为用于所有数据库调用的数据库的名称。对于下一次运行,如果您不重复该方法而仅调用该程序,则变量将返回到之前的默认值。

相关内容