Bash 中的命令替换(或者我所说的内联执行)$()
允许文本返回;在执行主命令之前。如果 $() 返回错误代码,如何中止主命令
更具体地说,我想停止/防止尝试进行辅助内联执行。
Echo $(ls /devb/*) is better than $(ls /dev/*)
即如果“ls /devb/”返回非零退出代码,我不想运行 /dev 列表,
我的真实示例变得相当冗长,具体细节对于问题来说并不是非常重要,但简化版本是
Connect_to_foreign_system 1.1.1.1 /u:$(zenity <prompt for user>) /p:$(zenity <prompt for password>)
如果取消用户提示,zenity 返回退出代码为 1,但主命令继续,然后提示输入密码。
如果第一次提示错误我真的不想提示输入密码,最好不要运行 Connect_to_foreign_system。
我没有在脚本中运行它,这是纯命令行,但即使我将它放在带有set -e
第二个内联命令和主命令的脚本中,仍然会运行。
我知道我可以编写一个脚本来获取每个文本字符串,然后运行命令并测试退出代码。我问如何将其作为独立声明来执行。
答案1
我会做:
cmd1_output=$(cmd1) && cmd2_output=$(cmd2) &&
printf '%s\n' "Both cmd1 and cmd2 succeeded and their output was respectively $cmd1_output and $cmd2_output"
所以在你的情况下:
user=$(zenity <prompt for user>) &&
password=$(zenity <prompt for password>) &&
Connect_to_foreign_system 1.1.1.1 /u:"$user" /p:"$password"
请注意,虽然$(...)
上面的 s 不需要在 bash 中引用,因为它们用于标量变量赋值,但与上面的$user
和一样$password
,它们在命令参数中使用时确实需要引用,因为它们会受到 split+glob 的影响如果不加引号。
请注意,一般来说,在命令行上传递密码是非常糟糕的做法,因为命令行参数通常在系统内是公共的(ps -Af
例如在输出中显示)。对于大多数命令,通常有一种更安全的方法来向它们传递机密,无论是通过环境变量还是通过某些文件或文件描述符。
$user
为了避免那些只使用一次的and变量污染变量命名空间$password
,您可以在子 shell ( ) 中运行整个 and-list (...)
:
(user=$(zenity <prompt for user>) &&
password=$(zenity <prompt for password>) &&
Connect_to_foreign_system 1.1.1.1 /u:"$user" /p:"$password")
或者,如果可以选择从 bash 切换到 zsh,则可以使用匿名函数的位置参数而不是变量:
() {
1=$(zenity <prompt for user>) &&
2=$(zenity <prompt for password>) &&
Connect_to_foreign_system 1.1.1.1 /u:$1 /p:$2
}
(在 zsh 中,split+glob 不会在未加引号的参数扩展上隐式执行,因此不需要引用这些$1
/ )。$2
如果只是在失败cmd2
时不运行cmd1
,但仍然使用 的输出cmd1
,在某些 shell 中,包括 ksh93、zsh 和 bash,但不是 dash、busybox sh、yash 或 mksh,您也可以这样做:
printf '%s\n' "output of cmd1: $(cmd1) and of cmd2 (not run if cmd1 failed) $([ "$?" -eq 0 ] && cmd2)"
答案2
这种内联执行称为命令替换。要检查某个命令的退出状态,您需要将其放在没有其他命令的命令上,而只是一个用于捕获输出的分配。例如:
if a=$(foo); then
echo >&2 "first command failed, exiting"
exit 1
fi
b=$(bar)
echo "output of first command: '$a', output of second: '$b'"
请注意,这$(ls /dev/*)
有点愚蠢,因为 shell 扩展了 glob /dev/*
,而ls
要做的就是打印 shell 提供的文件名。 (除非您也使用-l
或-F
来获取其他信息。)尽管ls
会列出 glob 匹配的任何子目录的内容。不管怎样,类似的事情可能值得重新考虑。如果您只想要 的第一级内容/dev
,则只需使用ls /dev
, 或echo /dev/*
代替。