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

Mik*_*ert 93 shell bash environment-variables

我刚刚遇到了几个答案,例如解析一个分隔的文本文件......使用构造:

while IFS=, read xx yy zz;do
    echo $xx $yy $zz
done < input_file
Run Code Online (Sandbox Code Playgroud)

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

我一直在阅读bash 参考,但无法弄清楚为什么这是合法的。

我试过

$ x="once upon" y="a time" echo $x $y
Run Code Online (Sandbox Code Playgroud)

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

der*_*ert 104

相关信息可以在 BASH 维护者提供的手册页上找到(上次检查时间为 2020 年 8 月)。Shell 语法部分,简单命令说明(强调):

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

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

$ x="once upon" y="a time" bash -c 'echo $x $y'
once upon a time
Run Code Online (Sandbox Code Playgroud)

  • 谢谢!在询问之前我做了很多谷歌搜索,并试图找出参考文献中提到的地方,但没有运气。我正在努力更好地编写 bash 脚本,你的回答很有帮助。 (2认同)
  • 一旦你知道序列,你甚至可以这样做:`x="once on"; x="a time" bash -c "echo $x \$x"`(不是你应该_,但这是一种测试你理解的有趣方式。) (2认同)

Mik*_*rst 46

定义的变量变得像分叉进程上的环境变量。

如果你跑

A="b" echo $A
Run Code Online (Sandbox Code Playgroud)

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

A="b" echo
Run Code Online (Sandbox Code Playgroud)

这是正确的方法:

x="once upon" y="a time" bash -c 'echo $x $y'
Run Code Online (Sandbox Code Playgroud)

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

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

for i in `TEST=test bash -c 'echo $TEST'`
do
  echo "TEST is $TEST and I is $i"
done
Run Code Online (Sandbox Code Playgroud)

将打印 TEST is and I is test

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

  • 谢谢,这正是我所希望的。投票没有听到您的赞赏更有意义! (2认同)

Hau*_*ing 11

man bash

环境

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

在变量赋值发生之前扩展变量。出于显而易见的原因,这var=x也会以另一种方式起作用,但var=$othervar不会。即您$x在它可用之前需要它。但这不是主要问题。主要问题是命令行只能被shell环境修改,而赋值不会成为shell环境的一部分。

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

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

x="once upon" y="a time" bash -c 'echo $x $y'
Run Code Online (Sandbox Code Playgroud)

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

  • 它比这更微妙,因为该示例也适用于 `x="once on" y="a time" eval 'echo $x $y'` 当不涉及子进程时,因为 `eval` 是内置的。我想手册页中的相关引用是`任何简单命令或函数的环境都可以通过在它前面加上参数分配来临时增加`。考虑到问题的示例,它必须是这种方式,因为 `read` 也是一个内置函数,并且它可以与临时更改的 `IFS` 状态一起使用。 (3认同)

che*_*ner 5

您提供的命令是不同的,因为$x并且在命令运行之前$y被扩展,所以它们在当前shell 中的值被使用,而不是在它的环境中看到的值。echoecho