在 bash 5.0 中,我希望捕获${PIPESTATUS[@]}
通过eval
. 但是,eval
似乎 mask ${PIPESTATUS[@]}
,但不 mask$?
相当于${PIPESTATUS[-1]}
。${PIPESTATUS[@]}
有没有办法从结果中提取eval
?在下面的命令字符串中添加一些内容似乎&& array=( ${PIPESTATUS[@]} ) && export array
不起作用。
我的假设是否正确,这$?
并不简单${PIPESTATUS[-1]}
?
我的示例代码(以 root 身份运行):
#!/usr/bin/env bash
#without eval, ${PIPESTATUS[@]} has two entries as it should.
apt-get install -y java-17-openjdk-amd64 2>&1 | tee -a ~/log
commandsPipestatus=( ${PIPESTATUS[@]} )
for status in ${commandsPipestatus[@]}; do
echo $status
done
echo ""
echo ""
#with eval, ${PIPESTATUS[@]} has one entry and is equal to $?
commandstring="apt-get install -y java-17-openjdk-amd64 2>&1 | tee -a ~/log"
eval "$commandstring"
commandsPipestatus=( ${PIPESTATUS[@]} )
for status in ${commandsPipestatus[@]}; do
echo $status
done
编辑:修复了我关于 PIPESTATUS 的原始声明中的一个小技术更正。另外,根据以下答案,这里有一些澄清:
- 我使用它是
eval
因为我正在以编程方式构建命令字符串,其中一些可能包含bash -c
...以不同用户身份运行命令。 - 我查看了设置
pipefail
,有时可能会起作用,但并不总是有效,因为有时我需要知道管道中多个步骤的状态。如果我可以设置set -o pipefail
然后取消设置它,那么就可以工作,但我不知道如何取消设置pipefail
,并且我不能简单地退出子 shell,然后继续进入一个pipefail
未设置的新子 shell。如何取消设置 shell 选项,例如pipefail
? - 我上面对如何导出包含的数组的理解是否
PIPESTATUS
错误?我怎样才能简单地PIPESTATUS
从eval
子shell中导出?
编辑2:感谢下面标记的出色答案,我的最终决定是针对我动态组装包含重定向的命令字符串的情况(这就是我需要 eval 的原因),我将使用 然后在执行后执行set -o pipefail
do set +o pipefail
。
我还在重新研究执行动态构建命令的最佳实践,因为自上次以来我已经有了几年的 bash 经验。我的用例eval
是:
- 命令可能包含
bash -c
,所以我不能用来bash -c
执行它们 - 可能包含重定向,例如
>> log
- 动态命令还可以添加参数以用于调试目的
据我所知,我可能可以对 1 做不同的事情,而对于 3 我应该使用像set -x [command]
and之trap
类的东西。我还没有找到2的解决方案。
$?
包含最后运行的命令的最右侧组件的状态(或最右侧失败组件,如果pipefail
已设置),尽管可能由!
前缀关键字修改。的元素
$PIPESTATUS
是前一个管道的每个组件的退出状态,!
对值没有影响。之后
(exit 1) | (exit 2) | (exit 3)
,$PIPESTATUS
将是 (1 2 3) 并且$?
将是 3。之后! (exit 1) | (exit 2) | (exit 3)
,$PIPESTATUS
将相同,但$?
将是 0,与 相同$(( ! 3 ))
。false
or(exit 1)
仍然是一个带有一个组件的管道,在第一种情况下是一个简单的命令,在第二种情况下是一个子 shell,所以你得到PIPESTATUS=(1)
and$?
= 1。这也是一样的,
eval 'any shell code'
只是一个简单的命令。之后
eval 'A | B' | C
,$PIPESTATUS
将包含 的退出状态eval
(这将是它运行的最后一个命令的退出状态:)B
和 的退出状态C
。(A | B) | C
顺便说一句,同样如此。你可以这样做:
要保留管道组件的状态(如 调用的解释器所见)
eval
,因此在您的情况下:然而在这里,在我看来你宁愿想要:
那是:
pipefail
选项,以便该函数在 或 失败时返回apt-get
失败tee
。请注意,函数的主体是一个子 shell,而不是更常见的命令组命令 ({ ...; }
),这意味着该选项仅在本地设置。使用最新版本的bash
,您还可以使用local -; set -o pipefail
在函数中本地设置的选项,而不需要子 shell。但请注意,正常输出和错误都
apt-get
将发送到 stdout。如果使用
zsh
,你可以这样做:将 stderr 的 stdout 发送
apt-get
到日志及其各自的原始目的地。ing在内部完成,并保留tee
退出状态。apt-get
也许可以为此创建一个辅助函数,例如:
使用函数代替
eval
可能是更好的解决方案。所以你可以这样做,如果需要的话允许安装多个包:
set -o pipefail
apt-get
是一种无需检查所有命令即可从命令中冒出错误的方法,但是如果您无法写入其日志PIPESTATUS
,事情可能会变得有点模糊。tee
编辑:基于附加信息
我猜测您遇到的问题
eval
是因为它在子 shell 中运行您的命令(或者至少以类似的方式运行)。感觉就像你只会得到一个退出代码(很高兴这个假设是错误的)。您可以在命令中打印管道状态,但获取它会很棘手。
这种怪物是你可以处理它的一种方法,但它非常复杂,并且在文本处理方面需要对你的命令进行一些定制,并且最后仍然只能得到一个退出代码
我觉得可以做一些事情来将输出
awk
输入readarray
以捕获不同的管道状态,但这将非常混乱(除了已经混乱之外)。当事情变得如此丑陋时,你必须开始怀疑整个方法是否错误。
也许如果你更详细地阐述你的需求,可能会出现一种不同的方式来实现它。