提问人:snovotill 提问时间:9/24/2023 最后编辑:snovotill 更新时间:9/30/2023 访问量:101
仅用空格而不是 ;分号;或 && 或 &&
Chaining bash or dash commands with just a space instead of ; semicolon ; or && or &&
问:
正常的命令链接方法可以正常工作,例如:
$ (SCRATCH='heya'; echo $SCRATCH)
heya
或者使用逻辑运算符 && 产生可预测的结果:
$ (SCRATCH='heya' && echo $SCRATCH)
heya
但是,如果我们使用空格来链接命令,那么事情就会变得很奇怪:
$ (SCRATCH='heya' echo $SCRATCH)
<<just a blank line prints here>>
尽管有上述情况,但变量实际上设置了 WAS,我可以用这一行来证明它:
$ (DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/$(id -u $USER)/bus" grdctl status)
RDP:
Status: enabled
TLS certificate: /home/guest/.local/share/gnome-remote-desktop/rdp-tls.crt
TLS key: /home/guest/.local/share/gnome-remote-desktop/rdp-tls.key
View-only: no
Username: (hidden)
Password: (hidden)
上面没有分号分隔两个命令,奇怪的是,它实际上以某种方式读取变量并起作用。
更奇怪的是,我可以通过插入正常的必要条件来破坏它 分号;这真的应该在那里,就像这样:
$ (DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/$(id -u $USER)/bus"; grdctl status)
Failed to lookup legacy VNC password schema: Cannot autolaunch D-Bus without X11 $DISPLAY
Failed to lookup RDP credentials: Cannot autolaunch D-Bus without X11 $DISPLAY
接下来,将冒犯的分号留在原地...... 我可以通过导出变量来使它恢复生机, 这让我认为 grdctl 命令将自己放入一个子 shell 中:
$ (export DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/$(id -u $USER)/bus"; grdctl status)
RDP:
Status: enabled
TLS certificate: /home/guest/.local/share/gnome-remote-desktop/rdp-tls.crt
TLS key: /home/guest/.local/share/gnome-remote-desktop/rdp-tls.key
View-only: no
Username: (hidden)
Password: (hidden)
然而,如果我们直接运行以下空格分隔的命令,那么变量就会消失在以太中,无影无踪,甚至没有任何错误!
$ SCRATCH='heya' echo $SCRATCH
<<just a blank line prints here>>
如果我导出变量,那么情况会变得更糟,甚至不会打印空行!
$ export SCRATCH='heya' echo $SCRATCH
<<nothing shows here, not even a blank line now>>
#And again for good measure, same result:
$ export SCRATCH='heya' echo $SCRATCH
<<still nothing, same as before>>
#But yeah, the variable did export to my shell because:
$ echo $SCRATCH
heya
因此,分号 send 不仅使 echo 无法访问变量,而且还会阻止 echo 看到父环境!
而且不仅仅是内置的 echo 命令被灌注,printf 也生病了:
$ EPHEMERAL='blink' printf "$EPHEMERAL\n"
<<again, a suffocated blank line here>>
但是 grdctl 命令是一个二进制文件(既不是脚本也不是内置文件),非常乐意摄取我的变量(不带分号),并正确输出:
$ unset DBUS_SESSION_BUS_ADDRESS
$ DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/$(id -u $USER)/bus" grdctl status
RDP:
Status: enabled
TLS certificate: /home/guest/.local/share/gnome-remote-desktop/rdp-tls.crt
TLS key: /home/guest/.local/share/gnome-remote-desktop/rdp-tls.key
View-only: no
Username: (hidden)
Password: (hidden)
上述行为似乎非常不一致,因为 echo 命令的行为方式是另一种方式,而 grdctl 的行为方式是另一种方式。
我不停地用谷歌搜索并咨询人工智能,但没有人能解释命令与空格的链式连接,这绝对是“一回事”,因为它经常有效。
互联网上有数以万计的解释;或 && 或 ||可用于链接命令,但不能窥视空格字符。
--------------我的最终更新--------------
好的,基于下面的所有良好讨论,这是解决方法#1:
$ YUCK=pid; YUCK=stu sh -c 'echo -n $YUCK';echo $YUCK
这是解决方法#2:
$ echo 'printf $SOMEVAR"\n"' > myscript.sh; chmod u+x myscript.sh
$ SOMEVAR='blink' ./myscript.sh
显然,有些命令需要变通方法,而另一些则不需要。
答:
事实是,bash 允许在执行命令之前“动态”定义导出的临时环境变量。
SOME_VAR_TO_BE_EXPORTED="value only for the command" some_command
例:
echo 'printf "MY_ENV_VAR=%s" $MY_ENV_VAR\n' > myscript.sh
chmod a+x ./myscript.sh
现在让我们使用带前缀的 env var 定义来执行它
# just to be sure
unset MY_ENV_VAR
MY_ENV_VAR=value ./myscript.sh
将输出:
MY_ENV_VAR=value
但范围仅在调用期间存在(假设之前未设置)$MY_ENV_VAR
echo $MY_ENV_VAR
应为空
这里有一些解释:
https://www.baeldung.com/linux/set-env-variables-bash-command
官方文档在这里
https://www.gnu.org/software/bash/manual/html_node/Environment.html
该段:
任何简单命令或函数的环境都可以通过在环境前面加上参数赋值来临时扩充,如 Shell 参数中所述。这些赋值语句仅影响该命令所看到的环境。
所以是的,这是一个令人困惑的语法。这里缺少分号是故意用于此特定语法的,仅适用于 envvar 分配。
分号将产生一个不同的行为:2 条指令。所以定义变量,没有必要也导出它,然后执行命令(最终在执行第二条指令之前扩展变量)
怎么样。。。什么都打印不出来?EPHEMERAL='blink' printf "$EPHEMERAL\n"
好吧,临时 envvar 导出可以解释为以下伪代码:
old_EPHEMERAL=$EPHEMERAL
export EPHEMERAL='blink'
# assuming `printf` is any command or function to call here
printf "$old_EPHEMERAL\n"
if [[ -n $old_EPHEMERAL ]]
then
EPHEMERAL=$old_EPHEMERAL
fi
# the old value, was only mentioned for the pseudo-code
unset old_EPHEMERAL
但多合一!
这意味着在评估多合一时尚不可用。export EPHEMERAL='blink'
因此,当评估(尚未调用)时,值是导出发生之前的任何值。然后,使用参数调用该命令,并且 envvar 也变为可用(在调用期间具有值)printf "$EPHEMERAL\n"
$EPHEMERAL
blink
一个不见了,我很好奇,我终于明白了:
打印什么都没有,即使有旧值。export SCRATCH='heya' echo $SCRATCH
$SCRATCH
export
是一个内置命令,它以一些变量名称并更改其导出状态作为参数。它可以接受多个参数,有赋值和无赋值。它不输出任何内容,并且大多数情况下总是返回 0(成功),即使在有错误的赋值中也是如此。它只在分配失败的罕见用例中返回失败状态:例如无效的变量名称、系统故障(如内存不足)或只读变量赋值等。请参见:SC2155$?
v=$(false)
在下面的伪代码中分解也是如此,仍然是一体式的。⚠️ 注意:这不会被识别为临时增强的 prefixing-with-parameter,因为它被评估为命令。export SCRATCH='heya' echo $SCRATCH
# pseudo code decomposition:
# export and assign $SCRATCH
export SCRATCH='heya'
# export a variable named $echo
export echo
# export a variable from the expansion of the old value of $SCRATCH
export $SCRATCH
够棘手吗?😉
您可以使用以下额外命令查看结果:
export -p | grep -E 'echo|SCRATCH|heya'
# outputs
declare -x SCRATCH="heya"
declare -x echo
如果运行两次,它会输出什么?(留给读者作为练习)export SCRATCH='heya' echo $SCRATCH
使用 bash 版本 5.1.16 进行测试(有关官方手册部分,另请参阅上面⬆️的 bash 链接: 3.7.4 环境)
希望对您有所帮助,请继续阅读文档,并且可能会查看 shellcheck,😉您可能会学到更多技巧。
想成为一名命令行战士吗?愿本站仍供稿
https://www.commandlinefu.com/commands/browse/sort-by-votes
评论
echo
不是外部可执行文件,也不会从其环境中读取变量值。它是一个内置的。您不是在用空格链接 - 您正在尝试在可执行文件的环境中设置变量的值。foo=12 some_program
在 的环境中将值设置为 12。 执行相同的操作,只是变量及其值在任何后续可执行文件的环境中都可用。 是一个内置的。它不会生成,因此不会获得新环境。它也不会从环境中读取变量。foo
some_program
export foo=12
echo
printf
printf
type -a printf
whereis printf
printf --version
printf
/usr/bin/printf --version
printf