ChatGPT解决这个技术问题 Extra ChatGPT

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

在给定的 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
我希望其中一个答案也能解释为什么这种方法有效,即为什么它不等于在调用之前导出变量。

D
Dennis Williamson
FOO=bar bash -c 'somecommand someargs | somecommand2'

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

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

(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(可能不是 bash,但可能是其他东西,例如 dash)而且我没有遇到任何问题如果我必须在命令 args (someargs) 中使用引号,则麻烦。
T
Teemu Leisti

您还可以使用 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 解决方案更清晰,虽然有些冗长。
b
benjimin

使用 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 的全部目的是解决所述问题。
A
Akhil

一个简单的方法是使用 ;

例如:

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 添加到同一行将使其成为一个衬里。但我忽略了它,因为它没有意义。
P
Peter Mortensen

使用 shell 脚本:

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

> ./myscript

你还需要export;否则 $FOO 将是 shell 变量,而不是环境变量,因此对 somecommandsomecommand2 不可见。
它可以工作,但它违背了拥有单行命令的目的(我正在尝试学习更多创造性的方法来避免相对简单的情况下的多行和/或脚本)。和@Keith 所说的一样,尽管至少导出将保持在脚本的范围内。
@KeithThompson 你的评论让我学到了一些新东西。我从没想过 shell 与环境变量。我曾经考虑过全局环境变量和局部环境变量。现在我知道正确的术语了。