在Bash中的命令对管道中的第二个命令不起作用之前设置环境变量


351

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

FOO=bar somecommand someargs

这行得通...有点。当您更改LC_ *变量(这似乎会影响命令,但不影响其参数,例如'[az]'char范围)时,或者在将输出管道输送到另一个命令时,此命令将无效:

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

我还可以在somecommand2前面加上“ FOO = bar”,它可以工作,但是会增加不必要的重复,并且对于根据变量解释的参数(例如,“ [az]”)无济于事。

那么,单行执行此操作的好方法是什么?

我在想一些事情:

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

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


1
(T=$(date) echo $T)将会工作
vp_arth 2015年

在跨平台(包括Windows)脚本或基于npm的项目(js或其他)的上下文中,您可能想看看cross-env模块
Frank Nocke

5
我希望其中一个答案也能解释为什么这仅是一种工作,即为什么它不等同于在调用之前导出变量。
Brecht Machiels

Answers:


317
FOO=bar bash -c 'somecommand someargs | somecommand2'

2
这满足了我的标准(不需要“导出”的单线)...我认为没有调用“ bash -c”(例如,创造性地使用括号)就无法做到这一点吗?
MartyMacGyver 2012年

1
@MartyMacGyver:我想不到的。花括号也不能使用。
暂停,直到另行通知。

7
请注意,如果您需要以somecommandsudo 身份运行,则需要通过sudo -E标志传递变量。因为变量可能会引入漏洞。stackoverflow.com/a/8633575/1695680
ThorSummoner 2015年

11
请注意,如果您的命令已具有两个引号级别,则由于使用引号地狱,此方法将变得非常不令人满意。在这种情况下,使用subshel​​l进行导出要好得多。
Pushpendre

奇怪:在OSX上FOO_X=foox bash -c 'echo $FOO_X'可以正常工作,但是使用特定的var名称会失败:DYLD_X=foox bash -c 'echo $DYLD_X'回显空白。两者都使用eval代替bash -c
mwag

209

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

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

Keith有一点要无条件执行命令,请执行以下操作:

(export FOO=bar; somecommand someargs | somecommand2)

15
我用;而不是&&; 没有办法export FOO=bar会失败。
基思·汤普森

1
@MartyMacGyver:&&执行左命令,然后仅在左命令成功后才执行右命令。;无条件地执行两个命令。在Windows批(cmd.exe的)相当于;&
基思·汤普森

2
在zsh中,我似乎不需要此版本的输出:(FOO=XXX ; echo FOO=$FOO) ; echo FOO=$FOOyields FOO=XXX\nFOO=\n
猖ion

3
@PopePoopinpants:在这种情况下为什么不使用source(aka .)?同样,这些天不应该再使用反引号了,这也是使用$(command)waaaaay更安全的原因之一。
0xC0000022L 2014年

3
如此简单,却如此优雅。我喜欢您的答案胜于接受的答案,因为它会启动一个与我当前的子shell相等的子shell(可能不是,bash但可能是其他东西,例如dash),而且如果必须使用引号,也不会遇到任何麻烦在命令args(someargs)中。
梅基2014年

45

您也可以使用eval

FOO=bar eval 'somecommand someargs | somecommand2'

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

当我们得到一些常规的意见时,最好是提供一种替代方法来eval取悦所有人,并拥有此快速eval“窍门”的所有好处(甚至更多!)。只需使用一个功能!使用所有命令定义一个函数:

mypipe() {
    somecommand someargs | somecommand2
}

并使用如下环境变量执行它:

FOO=bar mypipe

7
@Alfe:您是否也否决了接受的答案?因为它表现出与相同的“问题” eval
gniourf_gniourf

10
@Alfe:不幸的是,我不同意您的批评。此命令是绝对安全的。你真的听起来像一个曾经读过eval邪恶但不了解邪恶的人eval。也许您毕竟不是真的很了解这个答案(而且确实没有错)。在同一水平上:您会说ls不好是因为 for file in $(ls),不好吗?(是的,您没有否决接受的答案,也没有发表评论)。SO有时是一个如此奇怪和荒谬的地方。
gniourf_gniourf '16

7
@Alfe:当我说您听起来真的像一个曾经读过eval邪恶但不了解邪恶的人时eval我指的是您的句子:这个答案缺少谈论时所有必要的警告和解释eval eval不坏或危险;不超过bash -c
gniourf_gniourf

1
除了投票外,@ Alfe提供的评论某种程度上暗示接受的答案更安全。本来可以帮助您描述您认为对的使用不安全的内容eval。在提供的答案中,args被单引号保护以防止变量扩展,因此我认为答案没有问题。
布雷特·瑞安

我删除了我的评论,以将我的关注点集中在一个新的评论上:这 eval是一个总体上的安全问题(就像bash -c但不太明显),因此在提出使用建议的答案中应提及这些危险。粗心的用户可能会采纳答案(FOO=bar eval …)并将其应用于他们的情况,从而引起问题。但是,对于回答者来说,弄清楚我是否对他和/或其他答案是否赞成比对任何事情进行改进显然更为重要。如我之前所写,公平不应该成为主要关注点;是不差比其他任何给定的答案也是irregardless。
Alfe

12

使用env

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

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

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

-5

使用shell脚本:

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

> ./myscript

13
你还需要export; 否则$FOO将是shell变量,而不是环境变量,因此对somecommand或不可见somecommand2
基思·汤普森

它可以工作,但是却无法实现单行命令的目的(在相对简单的情况下,我试图学习更多的创造性方法来避免使用多行代码和/或脚本)。@Keith所说的话,尽管至少导出将只限于脚本。
MartyMacGyver 2012年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.