在bash中的命令之前设置环境变量不适用于管道中的第二个命令

Mar*_*ver 333 bash environment-variables

在给定的shell中,通常我会设置一个或多个变量,然后运行一个命令.最近我了解了将变量定义添加到命令的概念:

FOO=bar somecommand someargs
Run Code Online (Sandbox Code Playgroud)

这工作......有点儿.当你改变一个LC_*变量(它似乎影响命令而不是它的参数,例如'[az]'字符范围)或者输出到另一个命令时,它不起作用:

FOO=bar somecommand someargs | somecommand2  # somecommand2 is unaware of FOO
Run Code Online (Sandbox Code Playgroud)

我可以在"FOO = bar"之前添加somecommand2,但是它可以添加不需要的重复,但它对根据变量解释的参数没有帮助(例如'[az]')

那么,在一条线上做这件事的好方法是什么?我正在考虑以下顺序:

FOO=bar (somecommand someargs | somecommand2)  # Doesn't actually work
Run Code Online (Sandbox Code Playgroud)

编辑:我有很多好的答案!目标是保持一个单行,最好不使用"导出".使用bash调用的方法总体上是最好的,尽管带有"export"的括号版本更紧凑.使用重定向而不是管道的方法也很有趣.

Pau*_*ce. 295

FOO=bar bash -c 'somecommand someargs | somecommand2'
Run Code Online (Sandbox Code Playgroud)

  • 请注意,如果您的命令已经有两个级别的引号,那么这个方法因为引用地狱而变得非常不令人满意.在那种情况下,子shell中的导出要好得多. (9认同)
  • 注意,如果你需要将`somecommand`作为sudo运行,你需要传递sudo`-E`标志来传递变量.因为变量可能会引入漏洞.http://stackoverflow.com/a/8633575/1695680 (6认同)
  • 这满足了我的标准(不需要“导出”的单线)...我认为没有办法*不*调用“ bash -c”(例如,创造性地使用括号)? (2认同)
  • @MartyMacGyver:我想不到。它也不适用于花括号。 (2认同)

0xC*_*22L 191

如何导出变量,但仅在子shell中?:

(export FOO=bar && somecommand someargs | somecommand2)
Run Code Online (Sandbox Code Playgroud)

Keith有一个观点,无条件执行命令,执行此操作:

(export FOO=bar; somecommand someargs | somecommand2)
Run Code Online (Sandbox Code Playgroud)

  • 我会用`;`而不是`&&`; 没有办法`出口FOO = bar`将会失败. (12认同)
  • @MartyMacGyver:“&&”执行左侧命令,然后仅当左侧命令成功时才执行右侧命令。`;` 无条件执行这两个命令。Windows 批处理 (`cmd.exe`) 相当于 `;` 的是 `&`。 (4认同)
  • @PopePoopinpants:为什么不在这种情况下使用`source`(又名`.`)?此外,这些天不应再使用反引号了,这也是使用`$(command)`更安全的原因之一. (3认同)
  • 在zsh中,我似乎不需要导出此版本:`(FOO = XXX; echo FOO = $ FOO); echo FOO = $ FOO`产生`FOO = XXX \nFOO = \n`. (2认同)
  • 如此简单,又如此优雅.而且我比你接受的答案更喜欢你的答案,因为它会启动一个等于我当前的子shell(可能不是'bash`但可能是其他东西,例如`dash`)而且我没有碰到任何麻烦,如果我必须在命令args(`someargs`)中使用引号. (2认同)

gni*_*urf 42

您还可以使用eval:

FOO=bar eval 'somecommand someargs | somecommand2'
Run Code Online (Sandbox Code Playgroud)

由于这个答案eval似乎并不能让所有人满意,所以让我澄清一下:当使用单引号书面时,它是完全安全的.这很好,因为它不会启动外部进程(如接受的答案),也不会在额外的子shell中执行命令(如另一个答案).

当我们得到一些常规观点时,给予eval每个人满意的替代方案可能是好的,并且具有这种快速eval"技巧"的所有好处(甚至可能更多!).只需使用一个功能!使用所有命令定义函数:

mypipe() {
    somecommand someargs | somecommand2
}
Run Code Online (Sandbox Code Playgroud)

并使用您的环境变量执行它,如下所示:

FOO=bar mypipe
Run Code Online (Sandbox Code Playgroud)

  • @Alfe:不幸的是,我不同意你的批评.这个命令非常安全.你真的听起来像曾经读过_`eval`的人是邪恶的,而不理解`eval`的邪恶.也许你毕竟不是真的理解这个答案(而且它确实没有任何问题).在同一级别:你会说`ls'是坏的,因为`for $(ls)`中的文件是坏的?(是的,你没有对所接受的答案进行投票,你也没有发表评论).所以有时候这是一个奇怪而荒谬的地方. (10认同)
  • @Alfe:当我说_你真的听起来像曾经读过'eval`的人是邪恶而不理解'eval'的邪恶,_我指的是你的句子:_这个答案缺乏谈论时所需的所有警告和解释`eval`._`eval`并不坏或危险; 不过是'bash -c`. (7认同)
  • @Alfe:您是否还接受了接受的答案?因为它表现出与"eval"相同的"问题". (6认同)

Akh*_*gam 12

一个简单的方法是利用 ;

例如:

ENV=prod; ansible-playbook -i inventories/$ENV --extra-vars "env=$ENV"  deauthorize_users.yml --check
Run Code Online (Sandbox Code Playgroud)

command1; command2在执行 command1 之后,依次执行 command2。命令是否成功并不重要。

  • 它之所以有效,是因为它在执行分号后面的命令的同一 shell 环境中定义了“ENV”。不过,这与其他答案的不同之处在于,这个答案为 shell 中的_所有_后续引用定义了“ENV”,而不仅仅是同一行上的引用。我相信最初的问题只是为了改变同一行上的引用的环境。 (9认同)
  • 酷,为什么会这样? (4认同)

ben*_*min 6

使用env

例如,env FOO=BAR command。请注意,command完成执行后,环境变量将再次恢复/不变。

请注意外壳替换的发生,即,如果要$FOO在同一命令行上显式引用,则可能需要对其进行转义,以便您的外壳解释程序运行之前不执行替换env

$ export FOO=BAR
$ env FOO=FUBAR bash -c 'echo $FOO'
FUBAR
$ echo $FOO
BAR
Run Code Online (Sandbox Code Playgroud)

  • 这是完全正确的答案,“env”的全部目的就是解决上述问题。 (2认同)