【问题标题】:Setting an environment variable before a command in Bash is not working for the second command in a pipe在 Bash 中的命令之前设置环境变量不适用于管道中的第二个命令
【发布时间】:2012-06-07 00:57:46
【问题描述】:

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

FOO=bar somecommand someargs

这行得通……有点。当您更改 LC_* 变量(这似乎会影响命令,但 它的参数,例如,'[az]' 字符范围)或将输出传递到因此,另一个命令:

FOO=bar somecommand someargs | somecommand2  # somecommand2 is unaware of FOO

我也可以在 somecommand2 前面加上“FOO=bar”,这可行,但会增加不必要的重复,并且对于根据变量解释的参数没有帮助(例如,'[az]') .

那么,在一行中执行此操作的好方法是什么?

我正在考虑以下顺序:

FOO=bar (somecommand someargs | somecommand2)  # Doesn't actually work

我得到了很多很好的答案!目标是保持单线,最好不使用“导出”。使用 Bash 调用的方法总体上是最好的,尽管带有“导出”的括号版本更紧凑一些。使用重定向而不是管道的方法也很有趣。

【问题讨论】:

  • (T=$(date) echo $T) 将工作
  • 在跨平台(包括 windows)脚本或基于 npm 的项目(js 或其他)的上下文中,您可能需要查看cross-env module
  • 我希望其中一个答案也能解释为什么这只是一种工作方式,即为什么它不等同于在调用之前导出变量。

标签: bash environment-variables


【解决方案1】:

一个简单的方法是利用;

例如:

ENV=prod; ansible-playbook -i inventories/$ENV --extra-vars "env=$ENV"  deauthorize_users.yml --check

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

【讨论】:

  • 之所以有效,是因为它在分号后面的命令执行的同一 shell 环境中定义了ENV。但是,这与其他答案的不同之处在于,这个答案为 shell 中的 all 后续引用定义了ENV,而不仅仅是同一行中的引用。我相信最初的问题只是为了改变同一行上的引用的环境。
  • ;unset ENV 添加到同一行将使其成为一行。但我忽略了它,因为它没有意义。
【解决方案2】:

使用 shell 脚本:

#!/bin/bash
# myscript
FOO=bar
somecommand someargs | somecommand2

> ./myscript

【讨论】:

  • 你还需要export;否则 $FOO 将是一个 shell 变量,而不是环境变量,因此对 somecommandsomecommand2 不可见。
  • 它可以工作,但它违背了拥有单行命令的目的(我正在尝试学习更多创造性的方法来避免相对简单的情况下的多行和/或脚本)。和@Keith 所说的一样,尽管至少导出将保持在脚本的范围内。
【解决方案3】:

使用env

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

请注意shell替换的发生,即如果您想在同一命令行上明确引用$FOO,您可能需要对其进行转义,以便您的shell解释器不会执行替换之前 它运行env

$ export FOO=BAR
$ env FOO=FUBAR bash -c 'echo $FOO'
FUBAR
$ echo $FOO
BAR

【讨论】:

  • 这是完全正确的答案,env 的全部目的是解决所述问题。
【解决方案4】:

你也可以使用eval:

FOO=bar eval 'somecommand someargs | somecommand2'

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

当我们获得一些常规视图时,最好为eval 提供一个可以取悦所有人的替代方案,并且拥有这个快速eval“技巧”的所有好处(甚至更多!)。只需使用一个功能!使用所有命令定义一个函数:

mypipe() {
    somecommand someargs | somecommand2
}

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

FOO=bar mypipe

【讨论】:

  • @Alfe:您是否也对接受的答案投了反对票?因为它表现出与eval 相同的“问题”。
  • @Alfe:很遗憾,我不同意你的批评。这个命令是完全安全的。你真的听起来像一个曾经读过eval是邪恶的却不了解eval的邪恶之处的人。也许您毕竟并没有真正理解这个答案(而且确实没有任何问题)。在同一水平上:你会说ls 很糟糕,因为for file in $(ls) 很糟糕吗? (是的,您没有否决接受的答案,也没有发表评论)。 SO 有时是一个如此奇怪和荒谬的地方。
  • @Alfe:当我说你真的听起来像一个曾经读过eval是邪恶的人,却不了解eval的邪恶之处,我指的是你的句子:这个答案在谈论eval 时缺少所有必要的警告和解释。 eval 不错或很危险;不超过bash -c
  • 除了投票之外,@Alfe 提供的评论确实暗示着接受的答案在某种程度上更安全​​。对您来说,描述您认为使用eval 不安全的地方会更有帮助。在提供的答案中,args 被单引号引起来防止变量扩展,所以我认为答案没有问题。
  • 我认为函数解决方案比eval 解决方案更清晰,虽然有些冗长。
【解决方案5】:
FOO=bar bash -c 'somecommand someargs | somecommand2'

【讨论】:

  • 这符合我的标准(不需要“导出”的单行)...我认为没有办法做到这一点没有调用“bash -c”(例如, 创造性地使用括号)?
  • @MartyMacGyver:我想不到。它也不适用于花括号。
  • 请注意,如果您需要将 somecommand 作为 sudo 运行,则需要通过 sudo 传递 -E 标志来传递变量。因为变量会引入漏洞。 stackoverflow.com/a/8633575/1695680
  • 请注意,如果您的命令已经有两级引号,那么由于引号地狱,此方法变得非常不令人满意。在这种情况下,在 subshel​​l 中导出要好得多。
  • 奇数:在 OSX 上,FOO_X=foox bash -c 'echo $FOO_X' 按预期工作,但使用特定的 var 名称会失败:DYLD_X=foox bash -c 'echo $DYLD_X' 回显空白。两者都使用 eval 而不是 bash -c
【解决方案6】:

如何导出变量,但只在子外壳内?:

(export FOO=bar && somecommand someargs | somecommand2)

Keith 有一点,要无条件地执行命令,这样做:

(export FOO=bar; somecommand someargs | somecommand2)

【讨论】:

  • 我会使用; 而不是&&; export FOO=bar 不会失败。
  • @MartyMacGyver: && 执行左侧命令,然后仅在左侧命令成功时执行右侧命令。 ; 无条件执行这两个命令。 Windows 批处理 (cmd.exe) 等效于 ;&
  • 在 zsh 中,我似乎不需要此版本的导出:(FOO=XXX ; echo FOO=$FOO) ; echo FOO=$FOO 产生 FOO=XXX\nFOO=\n
  • @PopePoopinpants:在这种情况下,为什么不使用source(又名.)?此外,这些天不应再使用反引号,这就是为什么使用 $(command) 更安全的原因之一。
  • 如此简单,却又如此优雅。而且我比接受的答案更喜欢你的答案,因为它会启动一个与我当前的子shell相等的子shell(可能不是bash,但可能是其他东西,例如dash)而且我没有遇到任何问题如果我必须在命令 args (someargs) 中使用引号,则麻烦。
猜你喜欢
  • 2015-12-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-22
  • 2012-12-11
  • 2018-03-20
  • 1970-01-01
  • 2014-11-29
相关资源
最近更新 更多