Setting an environment variable before a command in bash not working for second command in a pipe
在给定的shell中,通常我会设置一个或多个变量,然后运行一个命令。最近,我了解了将变量定义前置到命令的概念:
1
| FOO=bar somecommand someargs |
这工作…有点。当您更改一个lc_*变量(它似乎会影响命令,但不会影响命令的参数,例如'[a-z]'char ranges),或者将输出管道传输到另一个命令时,它不起作用:
1
| FOO=bar somecommand someargs | somecommand2 # somecommand2 is unaware of FOO |
我也可以在somecommand2前面加上"foo=bar",这是有效的,但会增加不必要的重复,而且对于根据变量解释的参数也没有帮助(例如"a-z")。
那么,在一条线上做这件事的好方法是什么?我在考虑以下几点:
1
| FOO=bar (somecommand someargs | somecommand2) # Doesn't actually work |
编辑:我有很多好答案!我们的目标是保持这一水平,最好不要使用"出口"。使用对bash的调用的方法总体上是最好的,尽管带有"export"的附加版本稍微紧凑一些。使用重定向而不是管道的方法也很有趣。
- (T=$(date) echo $T)将起作用
- 在跨平台(包括Windows)脚本或基于NPM的项目(JS或其他)的上下文中,您可能需要查看跨环境模块。
- 我希望其中一个答案也能解释为什么这只是一种工作方式,也就是为什么它不等于在调用之前导出变量。
- 这里解释的原因是:stackoverflow.com/questions/13998075/…
1
| FOO=bar bash -c 'somecommand someargs | somecommand2' |
- 这符合我的标准(一艘班轮不需要"出口")…我认为,如果不调用"bash-c"(例如,创造性地使用括号),就没有办法做到这一点?
- @马蒂马盖弗:没有我能想到的。它也不适用于花括号。
- 请注意,如果需要将somecommand作为sudo运行,则需要将-E标志传递给sudo以传递变量。因为变量会带来漏洞。stackoverflow.com/a/8633575/1695680
- 注意,如果您的命令已经有两个引号级别,那么这个方法会因为引号地狱而变得非常不令人满意。在这种情况下,在子shell中导出要好得多。
- 奇数:在OSX上,FOO_X=foox bash -c 'echo $FOO_X'按预期工作,但在特定的var名称中失败:DYLD_X=foox bash -c 'echo $DYLD_X'回显空白。两者都使用eval而不是bash -c工作。
- @Mwag:我对dyld一无所知("动态链接器"),但它显然关注以DYLD_开头的变量,它识别出一组变量,但抱怨自己不知道的变量。您所指的"失败"是由dyld输出的警告消息:"dyld:警告,未知环境变量:dyld_x"。注意,这是一个警告,而不是一个错误,变量的值实际上是输出的。
- @丹尼斯威廉姆森:我不是在调用dyld,我是在调用echo,在我的系统中,它确实失败了:运行准确的、逐字的命令DYLD_X=foox bash -c 'echo $DYLD_X',根本没有得到回音(但如果我使用eval而不是bash -c)。
- @姆瓦格:我也没有调用dyld。它似乎自动被调用。你在运行什么版本的MacOS?bash --version说什么?我刚刚检查了一下,我上面报告的结果是因为我使用的是bash 5。当我使用/bin/bash时(bash 3.2),我没有收到任何警告,一个空行被回送(好像变量是空的)。不管怎样,动态加载器都在幕后做一些事情。
- 我在apple.stackexchange.com上问了一个问题。
- @丹尼斯威廉姆森,谢谢你注意到这一点并在另一个论坛提问。当我在3.2.57(1)-版本(x86_-Apple-Darwin17)上运行时,我得到一个空白,当我在3.2.57(1)-版本(x86_-Apple-Darwin14)上运行时,我得到警告+变量。
如何导出变量,但只在子shell内?:
1
| (export FOO=bar && somecommand someargs | somecommand2) |
基思有一点,要无条件地执行命令,请执行以下操作:
1
| (export FOO=bar; somecommand someargs | somecommand2) |
- 这似乎是可行的,虽然这不是我想要的(可能是没有办法做到我上面在一个命令中描述的事情,而不显式地使用export-first…)思想?
- 我用的是;,而不是&&;不可能export FOO=bar会失败。
- @基思-我总是想知道和(像大多数非文字问题一样)之间的区别,搜索起来并不容易……我知道Windows支持&;&;但是显然没有什么像";",fwiw)。我可能会发现自己使用这种结构或者bash结构,这取决于观众。
- @基思汤普森:好的,我把它编辑成了我的答案。谢谢。
- @martymacgyver:&&执行左命令,然后仅在左命令成功时执行右命令。;无条件地执行这两个命令。windows批量(cmd.exe)相当于;,是&。
- @基思汤普森-谢谢,这些信息会很有用的!
- 在zsh中,我似乎不需要导出这个版本:(FOO=XXX ; echo FOO=$FOO) ; echo FOO=$FOO生成FOO=XXX
FOO=
。
- 如果您有大量的环境变量,并且希望通过一个文件加载它们,那么您可以通过将您的所有export FOO=bar设置添加到一个文件中,然后像这样运行上面的内容来实现这一点:(`cat heroku_prod_config` && command some args)。
- @Popepopinpants:为什么不在这种情况下使用source(又称.)?而且,这些天不应该再使用backticks了,这也是为什么使用$(command)更安全的原因之一。
- @0xC00000022L:来源!我没想到。好多了。(我将编辑以反映)。还有,我第一次看到$(command)的时候,我得查一下…说到贝壳,我就不再是忍者了,我更是个农民……或农民。谢谢!
- 如此简单,却又如此优雅。我更喜欢你的回答,因为它将启动一个与我现在的答案相同的子shell(可能不是bash,但可能是其他东西,例如dash,如果我必须在命令args(someargs中使用引号,我不会遇到任何麻烦)。
- 我有一个bash脚本,其中导出了一系列环境变量,例如:export DB_PORT=3306—我启动了我的程序,其中包括env变量,如:source ./env.sh; ./some-program。
- 这是唯一适用于评估命令中相同变量的答案。例如,(export FOO=foo && echo"$FOO/bar/baz/$FOO")。
- 一种情况是,变量分配会失败,您可能不希望执行命令,如果该变量以前被标记为只读。
您也可以使用eval:
1
| FOO=bar eval 'somecommand someargs | somecommand2' |
由于这个对eval的回答似乎并不能让每个人都满意,所以让我澄清一点:当用单引号书写时,它是完全安全的。它不会启动外部进程(如接受的应答),也不会在额外的子shell中执行命令(如另一个应答),这很好。
当我们有一些固定的观点时,最好给每个人提供一个可以取悦所有人的eval的替代方案,并且有所有的好处(甚至更多!)这是一个很快的"骗局"。只需使用一个函数!使用所有命令定义函数:
1 2 3
| mypipe() {
somecommand someargs | somecommand2
} |
并使用这样的环境变量执行它:
- @阿尔夫:你也投了反对票吗?因为它表现出与eval相同的"问题"。
- @阿尔夫:不幸的是,我不同意你的批评。这个命令绝对安全。你听上去真像一个读过《以东记》的人,不知道《以东记》中的邪恶是什么。也许你根本就不理解这个答案(其实没有什么问题)。在同样的层面上:你会说ls是坏的,因为for file in $(ls)是坏的?(是的,你没有否决接受的答案,也没有发表评论)。有时候真是一个奇怪而荒谬的地方。
- @阿尔夫:当我说你听起来真的像一个读过《以东记》的人,不了解《以东记》中的邪恶是什么的时候,我指的是你的句子:这个答案缺乏谈论《以东记》中所有必要的警告和解释。eval不是坏的或危险的;不超过bash -c。
- 撇开投票权不谈,提供给@alfe的评论确实暗示了接受的答案在某种程度上更安全。如果你能描述一下你认为使用eval不安全的地方,会更有帮助。在提供的答案中,参数是单引号的,以防止变量扩展,因此我认为答案没有问题。
- 我删除了我的评论,将我的关注集中在一个新的评论中:eval一般来说是一个安全问题(如bash -c),但不太明显),因此建议使用它的答案中应该提到危险。粗心的用户可能会得到答案(FOO=bar eval …并将其应用到他们的情况中,从而引发问题。但很明显,对于回答者来说,弄清我是否对他的和/或其他答案投了反对票,比改善任何事情都重要。正如我之前写的,公平不应该是主要的关注点;不比任何其他给定的答案更糟糕也是毫无意义的。
- 到stackoverflow.com/questions/17529220/hellip;的链接足以提示危险。一个简短的陈述,哪方面的答案使它安全(单引号,所以不要使用没有他们!或者类似的)会更好。
- @阿尔夫:我通常做的是,当我发现答案是可以挽救的,但缺乏次要的材料(通常在这里和那里有一些引用),我只是默默地编辑它们。我只会在回答本身是错误的,并且表现出误解和反模式时做出负面评论和投反对票。您可以自由编辑此答案以添加安全备注。这里描述的eval方法不仅正确,而且是最好的选择,因为不需要子代码或子流程来完成任务。另外,我认为eval本身并不危险(不超过ls或其他命令)。您可以自由编辑!
- 当有人告诉我我的答案缺乏某些方面时,我不会因为想知道他们是否对我的答案投了反对票,以及他们是否也对其他答案投了反对票而责怪他。相反,我试图改进我的答案。但在如何处理事情上,我们显然有不同的方法。我发现避免交流和编辑别人的答案是第二个最好的解决方案,因为通常原始作者甚至没有注意到这一点,所以我不知道他们是否同意。我更喜欢达成共识,即使这意味着要踏入深渊。它并不总是有效。
使用shell脚本怎么样?
1 2 3 4 5 6
| #!/bin/bash
# myscript
FOO=bar
somecommand someargs | somecommand2
> ./myscript |
- 您仍然需要export;否则$FOO将是shell变量,而不是环境变量,因此对somecommand或somecommand2不可见。
- 这是可行的,但它违背了使用单行命令的目的(我正在尝试学习更多创造性的方法,以避免在相对简单的情况下使用多行程序和/或脚本)。而@keith所说的,尽管至少出口将保持在脚本的范围内。