请考虑以下代码段:
$ SOMEVAR=AAA
$ echo zzz $SOMEVAR zzz
zzz AAA zzz
我在第一行将$SOMEVAR
设置为AAA
- 当我在第二行回显它时,我按预期获得AAA
个内容。
但是,如果我尝试在与echo
相同的命令行上指定变量:
$ SOMEVAR=BBB echo zzz $SOMEVAR zzz
zzz AAA zzz
...我没有按照预期得到BBB
- 我得到旧值(AAA
)。
这是怎么回事?如果是这样,那么为什么你可以指定像LD_PRELOAD=/... program args ...
这样的变量并让它工作?我错过了什么?
答案 0 :(得分:87)
您看到的是预期的行为。问题是父shell在调用具有修改环境的命令之前在命令行上评估$SOMEVAR
。在设置环境之前,您需要延迟$SOMEVAR
的评估。
您的直接选择包括:
SOMEVAR=BBB eval echo zzz '$SOMEVAR' zzz
。SOMEVAR=BBB sh -c 'echo zzz $SOMEVAR zzz'
。这两个都使用单引号来阻止父shell评估$SOMEVAR
;它仅在环境中设置后进行评估(暂时,在单个命令的持续时间内)。
另一种选择是使用子shell表示法(Marcus Kuhn中answer建议):
(SOMEVAR=BBB; echo zzz $SOMEVAR zzz)
该变量仅在子shell中设置
答案 1 :(得分:33)
坦率地说,手册在这一点上令人困惑。 GNU Bash manual说:
任何简单命令或函数的环境[请注意,这不包括内置]可以通过在参数赋值前添加前缀来临时扩充,如Shell参数中所述。这些赋值语句仅影响该命令所见的环境。
如果您真的解析了句子,那么它所说的是命令/功能的环境被修改,而不是父进程的环境。所以,这将有效:
$ TESTVAR=bbb env | fgrep TESTVAR
TESTVAR=bbb
因为env命令的环境在执行之前已被修改。但是,这不起作用:
$ set -x; TESTVAR=bbb echo aaa $TESTVAR ccc
+ TESTVAR=bbb
+ echo aaa ccc
aaa ccc
因为shell执行参数扩展。
问题的另一部分是Bash defines these steps为其解释器:
这里发生的事情是内置程序没有自己的执行环境,因此它们永远不会看到修改后的环境。此外,简单命令(例如/ bin / echo)做获得修改的环境(这就是env示例有效的原因)但是shell扩展发生在当前中步骤#4中的环境。
换句话说,你没有将'aaa $ TESTVAR ccc'传递给/ bin / echo;您正在将插值字符串(在当前环境中展开)传递给/ bin / echo。在这种情况下,由于当前环境没有 TESTVAR ,您只需将'aaa ccc'传递给命令。
文档可能更清晰。好事就是堆栈溢出!
http://www.gnu.org/software/bash/manual/bashref.html#Command-Execution-Environment
答案 2 :(得分:19)
要实现您的目标,请使用
( SOMEVAR=BBB; echo zzz $SOMEVAR zzz )
原因:
您必须使用分号或新行与下一个命令分隔分配,否则在下一个命令(echo)发生参数扩展之前不会执行该分配。
< / LI>您需要在子shell 环境中进行分配,以确保它不会超出当前行。
此解决方案比其他一些解决方案更短,更整洁,更高效,特别是它不会创建新流程。
答案 3 :(得分:10)
原因是这为一行设置了一个环境变量。但是,echo
不会进行扩展,bash
会进行扩展。因此,即使在echo命令的上下文中SOME_VAR
为BBB
,您的变量实际上也会在命令执行之前展开。
要查看效果,您可以执行以下操作:
$ SOME_VAR=BBB bash -c 'echo $SOME_VAR'
BBB
此处变量在子进程执行之前不会展开,因此您会看到更新的值。如果你在父shell中再次检查SOME_VARIABLE
,它仍然是AAA
,正如预期的那样。
答案 4 :(得分:1)
SOMEVAR=BBB; echo zzz $SOMEVAR zzz
使用;分隔同一行的陈述。
答案 5 :(得分:1)
以下是另一种选择:
SOMEVAR=BBB && echo zzz $SOMEVAR zzz