sda*_*aau 83 bash environment-variables echo
请考虑以下代码段:
$ SOMEVAR=AAA
$ echo zzz $SOMEVAR zzz
zzz AAA zzz
Run Code Online (Sandbox Code Playgroud)
在这里,我设置$SOMEVAR
到AAA
第一线-当我赞同它的第二行,我得到的AAA
内容符合市场预期.
但是,如果我尝试在同一命令行上指定变量echo
:
$ SOMEVAR=BBB echo zzz $SOMEVAR zzz
zzz AAA zzz
Run Code Online (Sandbox Code Playgroud)
......我没有BBB
按照我的预期得到 - 我得到旧值(AAA
).
这是事情应该如何?如果是这样,那么为什么你可以指定变量LD_PRELOAD=/... program args ...
并让它工作?我错过了什么?
Jon*_*ler 92
你看到的是预期的行为.问题是父shell $SOMEVAR
在调用具有修改环境的命令之前在命令行上进行求值.$SOMEVAR
在设置环境之后,您需要获得延迟评估.
您的直接选择包括:
SOMEVAR=BBB eval echo zzz '$SOMEVAR' zzz
.SOMEVAR=BBB sh -c 'echo zzz $SOMEVAR zzz'
.这两个都使用单引号来阻止父shell评估$SOMEVAR
; 它仅在环境中设置后进行评估(暂时,在单个命令的持续时间内).
另一个选择是使用子壳表示法(Marcus Kuhn在他的回答中也提出了这个表示法):
(SOMEVAR=BBB; echo zzz $SOMEVAR zzz)
Run Code Online (Sandbox Code Playgroud)
该变量仅在子shell中设置
Tod*_*obs 33
坦率地说,手册在这一点上令人困惑.在GNU Bash的手册说:
任何简单命令或函数的环境[请注意,这不包括内置]可以通过在参数赋值前添加前缀来临时扩充,如Shell参数中所述.这些赋值语句仅影响该命令所见的环境.
如果你真的解析这句话,什么它说的是环境的命令/函数进行了修改,但不适用于父进程的环境.所以,这将工作:
$ TESTVAR=bbb env | fgrep TESTVAR
TESTVAR=bbb
Run Code Online (Sandbox Code Playgroud)
因为env命令的环境在执行之前已被修改.但是,这不起作用:
$ set -x; TESTVAR=bbb echo aaa $TESTVAR ccc
+ TESTVAR=bbb
+ echo aaa ccc
aaa ccc
Run Code Online (Sandbox Code Playgroud)
因为shell执行参数扩展时.
问题的另一部分是Bash 为其解释器定义了以下步骤:
这里发生的事情是内置程序没有自己的执行环境,因此它们永远不会看到修改后的环境.此外,简单的命令(例如/ bin/echo)确实获得了修改的环境(这就是env示例工作的原因)但是shell扩展是在步骤#4 中的当前环境中进行的.
换句话说,你没有将'aaa $ TESTVAR ccc'传递给/ bin/echo; 您正在将插值字符串(在当前环境中展开)传递给/ bin/echo.在这种情况下,由于当前环境没有TESTVAR,您只需将'aaa ccc'传递给命令即可.
文档可以更加清晰.好事就是Stack Overflow!
http://www.gnu.org/software/bash/manual/bashref.html#Command-Execution-Environment
Mar*_*uhn 20
为了达到你想要的效果,请使用
( SOMEVAR=BBB; echo zzz $SOMEVAR zzz )
Run Code Online (Sandbox Code Playgroud)
原因:
必须使用分号或新行与下一个命令分隔赋值,否则在下一个命令(echo)的参数扩展发生之前不会执行该赋值.
您需要在子shell环境中进行分配,以确保它不会超出当前行.
该解决方案比其他一些解决方案更短,更整洁,更高效,特别是它不会创建新的流程.
Fat*_*ror 10
原因是这为一行设置了一个环境变量.但是,echo
不做扩展,bash
确实如此.因此,在执行命令之前,您的变量实际上已展开,即使SOME_VAR
它BBB
位于echo命令的上下文中.
要查看效果,您可以执行以下操作:
$ SOME_VAR=BBB bash -c 'echo $SOME_VAR'
BBB
Run Code Online (Sandbox Code Playgroud)
这里变量在子进程执行之前不会展开,因此您可以看到更新的值.如果SOME_VARIABLE
再次检查父shell,它仍然AAA
如预期的那样.
让我们查看POSIX 规范以了解为什么它的行为如此,不仅在 bash 中,而且在任何兼容的 shell 中:
从规则 7(b) 开始,涵盖了在简单命令之前进行赋值的情况:
如果 '=' 之前的所有字符形成有效名称(参见 IEEE Std 1003.1-2001 的基本定义卷,第 3.230 节,名称),则应返回令牌 ASSIGNMENT_WORD。(带引号的字符不能参与形成有效名称。)
[...]
NAME 的分配应按照简单命令中的规定进行。
因此,对于符合 POSIX 的 shell 需要解析此分配。
重定向应按照重定向中的描述执行。
在分配值之前,每个变量分配都应针对波浪号扩展、参数扩展、命令替换、算术扩展和引号删除进行扩展。
[...]
如果没有命令名结果,变量赋值将影响当前的执行环境。否则,变量赋值应为命令的执行环境导出,不应影响当前执行环境(特殊内置程序除外)。如果任何变量赋值试图给只读变量赋值,就会发生变量赋值错误。有关这些错误的后果,请参阅 Shell 错误的后果。
因此: 必须导出一个简单命令的部分前缀中的赋值,并且不得影响“当前的 shell 环境”,除非被调用的命令是一个特殊的内置命令。此外,这些步骤应遵循重定向,重定向本质上必须发生在命令调用过程的后期。
应在包含以下内容的单独环境中调用除特殊内置程序(请参阅特殊内置实用程序)之外的实用程序。这些对象的初始值应与父 shell 的初始值相同,除非在下面注明。
[...]
具有导出属性的变量,以及在命令期间显式导出的变量,应传递给实用程序环境变量
因此:这些变量在 fork 之后和执行被调用的命令之前由子 shell 扩展,并且必须 - 根据规范 - 单独影响孩子的环境。
现在,对于一些不同的行为:
SOMEVAR=BBB sh -c 'echo "$SOMEVAR"'
Run Code Online (Sandbox Code Playgroud)
... 受益于sh
在启动时从其环境变量(如 POSIX 规范的第 2.5.3 节要求)创建 shell 变量的实例。
顺便说一下,值得注意的是,您所询问的语法是用于简单命令中的赋值,而不是子shell 中的赋值。您可以在管道中涉及的子shell中控制分配,如下所示:
{ SOMEVAR=BBB; echo "$SOMEVAR"; } | somecommand ...
Run Code Online (Sandbox Code Playgroud)
...将分配放入运行管道的第一个组件的子外壳中(如果您的外壳确实在子外壳中运行该组件,就 POSIX 而言,这是未定义的行为;来自规范:“作为扩展,但是,管道中的任何或所有命令都可以在当前环境中执行" )。
归档时间: |
|
查看次数: |
34062 次 |
最近记录: |