![如何将命令的输出分配给变量而不在子 shell 中运行该命令?](https://linux22.com/image/74644/%E5%A6%82%E4%BD%95%E5%B0%86%E5%91%BD%E4%BB%A4%E7%9A%84%E8%BE%93%E5%87%BA%E5%88%86%E9%85%8D%E7%BB%99%E5%8F%98%E9%87%8F%E8%80%8C%E4%B8%8D%E5%9C%A8%E5%AD%90%20shell%20%E4%B8%AD%E8%BF%90%E8%A1%8C%E8%AF%A5%E5%91%BD%E4%BB%A4%EF%BC%9F.png)
如果我有一个这样的函数:
TEST()
{
if [[ "$1" == "hi" ]]
then
exit 1
fi
echo "Some text"
}
如果我在当前 shell 中运行该函数:
TEST "hi"
一切都按预期进行。函数中的 if 语句将为 true,整个脚本将退出。另一方面,如果我这样做:
FUNCTION_OUTPUT=$(TEST "hi")
这样我就可以从变量中的函数捕获标准输出,函数内的 if 语句仍然为 true,“exit 1”仍然会触发,但由于我们在子 shell 中运行,脚本将继续运行。
有没有办法在当前 shell 中运行它,以便我的函数中的“exit 1”行实际上退出整个脚本,而不是使用 VAR_NAME=$() 在子 shell 中运行某些内容并将其分配给变量?
答案1
变量赋值只是一个简单命令,所以你可以使用如果条件检查函数是否成功或失败:
if ! FUNCTION_OUTPUT=$(TEST hi); then
echo Function return non-zero status
exit 1
fi
# This line never printed
printf '%s\n' "$FUNCTION_OUTPUT"
如果函数成功,您将获得FUNCTION_OUTPUT
包含函数结果的变量:
if ! FUNCTION_OUTPUT=$(TEST hii); then
echo Function return non-zero status
exit 1
fi
# Output content of FUNCTION_OUTPUT
printf '%s\n' "$FUNCTION_OUTPUT"
答案2
将命令重定向到文件并使用内置命令将文件读入变量,例如使用mapfile
.
答案3
有很多方法可以退出脚本。
VAR=$(FUNCTION) || kill -"$(($?&127))" 0
...可能是一种方式。无论返回的是什么,它都应该传递到父 shell 的进程组FUNCTION
。它并没有真正充实,但如果这就是您所要求的,您可以轻松地从命令替换中获得返回。另外,如果您需要有关在当前 shell 函数中分配变量的建议,您可以这样做:
fn(){ var=x; }; fn; echo "$var"
x
如果它必须是某个命令的输出,那么您正在谈论分层评估。您确实需要信任该输出,否则您需要捕获整个流,在这种情况下,您最好确定何时会收到它。这些事情通常是通过管道来完成的 - 这就是命令替换的工作方式 - 当然,还有子 shell。正如 shell 所做的那样,您必须read
按照其他地方的建议输入数据。
我喜欢使用自己的小管道 - 我一次向管道的缓冲区写入一点,并在准备好时再次读入。
fn(){ echo hey; read hey; } <> <(:) >&0
fn; echo "$hey"
hey
...我想我应该提到,如果你遵循最后一个例子,你应该特别小心不是来填充缓冲区。一次只写一点,并尽快阅读。几乎在任何系统上,您都可以依靠 512 字节的基线缓冲区(虽然不多),但它们通常更大。
如果您不小心,那么,作为该管道两端的所有者,您的 shell 在死锁时将不会从任何其他进程收到任何受挫的退出信号,并且它将永远卡住。我从来不会将任何非内置命令的输出指向这些文件描述符之一,除非我已经分叉了一个读取器进程来根据需要排空管道。
它使得相同的进程消息传递变得困难 - 您必须以完美的同步性精心管理它,并同时跟踪任务的所有单独线程。这就是 shell 分叉的原因。
答案4
我用:
tmp_fifo=$(mktemp)
mkfifo ${tmp_fifo}
exec 5<>${tmp_fifo}
assign() {
local var=$1;
shift
"$@" >&5
read ${var} <&5
}
assign myVar echo hola
echo $myVar
rm -f ${tmp_fifo}