看来变量“PIPESTATUS”在dash
.简单的单独执行不起作用,因为左侧命令产生非常大的输出。我使用 fifo 来完成此任务:
#!/bin/dash
mkfifo command1 command2
dash -c "cat ./content;code=\${?};echo \${code} > command1 &" | dash -c "md5sum;code=\${?};echo \${code} > command2 &"
echo "$(cat ./command1)" "$(cat ./command2)"
但不知道为什么挂了?
答案1
您可以使用命名管道并手动连接两个进程。以相反的顺序启动它们,因此左侧在前台运行,并且您可以$?
像往常一样获得其退出状态。
#!/bin/sh
dir=$(mktemp -d)
mkfifo "$dir/p"
cat < "$dir/p" > /dev/null &
( echo foo; exit 12; ) > "$dir/p"
echo "exit status: left: $?"
rm -r "$dir"
或者,如果您想要两者,请从 获取后台进程的 PID$!
并wait
获取退出状态。
#!/bin/sh
dir=$(mktemp -d)
mkfifo "$dir/p"
( echo foo; exit 12; ) > "$dir/p" & # left-hand side
pidleft=$!
( cat; exit 34; ) < "$dir/p" > /dev/null & # right-hand side
pidright=$!
wait "$pidleft"; exitleft=$?
wait "$pidright"; exitright=$?
echo "exit status: left: $exitleft right: $exitright"
rm -r "$dir"
你仍然可以将管道的第二部分留在前景,我只是想对称地进行。
您还可以将退出状态存储到文件中,然后从那里获取它们:
#/bin/sh
( somecmd; echo "$?" > exit1 ) | ( cat; echo "$?" > exit2)
echo "exit status: left: $(cat exit1) right: $(cat exit2)"
我认为命名管道在这里没有多大用处,因为退出状态只有几个字节。 shell 将等待管道完成,然后再尝试读取第二行上的exit1
and 。exit2
如果您想使用命名管道,则需要将管道放在后台,因为对管道的写入会阻塞,直到打开读取端。
#/bin/sh
mkfifo exit1 exit2
( somecmd; echo "$?" > exit1 ) | ( cat; echo "$?" > exit2) &
echo "exit status: left: $(cat exit1) right: $(cat exit2)"
但是,如果cat
读取管道的 s 由于某种原因没有运行,则写入管道的子 shell 将在后台无限期地阻塞。
答案2
您可以通过在实际命令退出后关闭管道左侧的标准输出和右侧的标准输入来取消挂起脚本:
例子:
#! /bin/dash
rm -f /tmp/s1 /tmp/s2
mkfifo /tmp/s1 /tmp/s2
{ (echo yes; exit 13); s1=$?; exec >&-; echo $s1 >/tmp/s1 & } | { (cat; exit 17); s2=$?; exec <&-; echo $s2 >/tmp/s2 & }
echo "`cat /tmp/s1` `cat /tmp/s2`"
将 替换(...; exit ..)
为您各自的命令。
在实际命令退出后关闭管道右侧的 stdin 会导致左侧的 write() 收到 SIGPIPE 或 EPIPE 而不是在尝试从右侧通过管道传输命令时发生阻塞(其本身在 open() 中被阻塞),而关闭左侧的 stdout 会导致管道右侧的 read() 收到 EOF 而不是尝试从左侧的echo ... >fifo &
阻塞进行管道传输。echo ... >fifo &
感谢@ilkkachu 的更正。