【问题标题】:Capture stdout to variable and get the exit statuses of foreground pipe将标准输出捕获到变量并获取前台管道的退出状态
【发布时间】:2020-05-07 12:29:04
【问题描述】:
我想执行一个命令(比如ls)和sed它的输出,然后将stdout保存到一个变量中,像这样,
OUT=$(ls | sed -n -e 's/regexp/replacement/p')
在此之后,如果我尝试访问$PIPESTATUS 数组,我只会得到0(与$? 相同)。那么,我怎样才能同时获得$PIPESTATUS 以及捕获整个管道命令的标准输出?
注意:
- 如果我只执行那些管道命令并且没有捕获标准输出(如
ls | sed -n -e 's/regexp/replacement/p'),我会在 $PIPESTATUS 中获得预期的退出状态(如 0 0)
- 如果我只使用Command Substitution 执行单个命令(没有管道多个命令)并捕获标准输出(如
OUT=$(ls)),我会在$PIPESTATUS 中得到预期的single 退出状态(即同$?)
附:我知道,我可以运行该命令 2 次(第一次捕获标准输出,第二次访问 $PIPESTATUS 而不使用命令替换),但是有没有办法在一次执行中同时获得两者?
【问题讨论】:
标签:
bash
shell
pipe
stdout
exit-code
【解决方案1】:
你可以:
-
使用临时文件传递 PIPESTATUS。
tmp=$(mktemp)
out=$(pipeline; echo "${PIPESTATUS[@]}" > "$tmp")
PIPESTATUS=($(<"$tmp")) # Note: PIPESTATUS is overwritten each command...
rm "$tmp"
-
使用临时文件传递out。
tmp=$(mktemp)
pipeline > "$tmp"
out=$(<"$tmp"))
rm "$tmp"
-
将输出与管道状态交错。例如,为 PIPESTATUS 保留从最后一个换行符到结尾的部分。为了保持原始返回状态,我认为需要一些临时变量:
out=$(pipeline; tmp=("${PIPESTATUS[@]}") ret=$?; echo $'\n' "${tmp[@]}"; exit "$ret"))
pipestatus=(${out##*$'\n'})
out="${out%$'\n'*}"
out="${out%%$'\n'}" # remove trailing newlines like command substitution does
测试:
out=$(false | true | false | echo 123; echo $'\n' "${PIPESTATUS[@]}");
pipestatus=(${out##*$'\n'});
out="${out%$'\n'*}"; out="${out%%$'\n'}";
echo out="$out" PIPESTATUS="${pipestatus[@]}"
# out=123 PIPESTATUS=1 0 1 0
注意事项: