告知左侧故障管道的右侧?
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了告知左侧故障管道的右侧?相关的知识,希望对你有一定的参考价值。
我喜欢在shell脚本中的函数之间使用类似生成器的模式。像这样的东西:
parse_commands /da/cmd/file | process_commands
但是,这种模式的基本问题是,如果parse_command遇到错误,我发现通知process_command它失败的唯一方法是明确告诉它(例如echo“FILE_NOT_FOUND”)。这意味着必须对parse_command中的每个可能的错误操作进行隔离。
难道没有办法使用非零退出代码检测左侧是否退出?
即使第一个流程已经结束,管道流程是否仍在继续,或者是您无法知道第一个流程失败的问题?
如果是后者,你可以查看PIPESTATUS
变量(实际上是一个BASH数组)。这将为您提供第一个命令的退出代码:
parse_commands /da/cmd/file | process_commands
temp=("${PIPESTATUS[@]}")
if [ ${temp[0]} -ne 0 ]
then
echo 'parse_commands failed'
elif [ ${temp[1]} -ne 0 ]
then
echo 'parse_commands worked, but process_commands failed'
fi
否则,您将不得不使用协同进程。
在bash脚本的顶部使用set -o pipefail
,这样当管道左侧出现故障(退出状态!= 0)时,右侧不会执行。
与运算符(&&)不同,管道运算符(|)通过同时生成两个进程来工作,因此第一个进程可以将其输出传递给第二个进程,而无需缓冲中间数据。这允许在几乎没有内存或磁盘使用的情况下处理大量数据。
因此,第一个进程的退出状态在第二个进程完成之前不可用。
您可以尝试使用fifo进行一些工作:
mkfifo /tmp/a
cat /tmp/a | process_commands &
parse_cmd /da/cmd/file > /tmp/a || (echo "error"; # kill process_commands)
我没有足够的声誉评论,但accepted answer错过了第5行的关闭}
。
修复此问题后,代码将抛出一个-ne: unary operator expected
错误,指出一个问题:PIPESTATUS
被if
命令后的条件覆盖,因此process_commands
的返回值永远不会被检查!
这是因为[ ${PIPESTATUS[0]} -ne 0 ]
是equivalent to test ${PIPESTATUS[0]} -ne 0
,它改变$PIPESTATUS
就像任何其他命令一样。例如:
return0 () { return 0;}
return3 () { return 3;}
return0 | return3
echo "PIPESTATUS: ${PIPESTATUS[@]}"
这会按预期返回PIPESTATUS: 0 3
。但是如果我们介绍条件呢?
return0 | return3
if [ ${PIPESTATUS[0]} -ne 0 ]; then
echo "1st command error: ${PIPESTATUS[0]}"
elif [ ${PIPESTATUS[1]} -ne 0 ]; then
echo "2nd command error: ${PIPESTATUS[1]}"
else
echo "PIPESTATUS: ${PIPESTATUS[@]}"
echo "Both return codes = 0."
fi
我们得到[: -ne: unary operator expected
错误,这个:
PIPESTATUS: 2
Both return codes = 0.
要解决这个问题,$PIPESTATUS
应该存储在不同的数组变量中,如下所示:
return0 | return3
TEMP=("${PIPESTATUS[@]}")
echo "TEMP: ${TEMP[@]}"
if [ ${TEMP[0]} -ne 0 ]; then
echo "1st command error: ${TEMP[0]}"
elif [ ${TEMP[1]} -ne 0 ]; then
echo "2nd command error: ${TEMP[1]}"
else
echo "TEMP: ${TEMP[@]}"
echo "All return codes = 0."
fi
哪个印刷品:
TEMP: 0 3
2nd command error: 3
如预期。
编辑:我修复了接受的答案,但我将这个解释留给子孙后代。
如果你有command1 && command2
那么command2只会在第一个命令成功时执行 - 否则布尔短路开始。使用它的一种方法是建立一个转储到临时的第一个命令(你的parse_commands...
)然后拥有从该文件获取第二个命令。
编辑:通过明智地使用;
,您可以整理临时文件,例如:
(command1 && command2) ; rm temporaryfile
在bash 4.0中有一种方法可以做到这一点,它添加了来自灰的coproc
内置。这个协同处理工具是从ksh借用的,它使用不同的语法。我在我的系统上访问的唯一支持coprocesses的shell是ksh。这是用ksh编写的解决方案:
parse_commands /da/cmd/file |&
parser=$!
process_commands <&p &
processor=$!
if wait $parser
then
wait $processor
exit $?
else
kill $processor
exit 1
fi
我们的想法是在后台启动parse_commands
,并将管道连接到主机壳。 pid保存在parser
中。然后process_commands
以parse_commands
的输出作为输入。 (这就是<&p
所做的。)这也被放在背景中,其pid保存在processor
中。
通过管道连接后台的两个,我们的主shell可以自由地等待解析器终止。如果它没有错误地终止,我们等待处理器完成并退出其返回码。如果它以错误终止,我们将终止处理器并以非零状态退出。
将此翻译为使用bash 4.0 / ash coproc
内置应该相当简单,但我没有好的文档,也没有测试方法。
你可以在一个明确的子shell中运行parse_commands /da/cmd/file
,并通过管道运行echo
这个子shell的退出状态process_commands
,它也运行在一个显式的子shell中来处理/dev/stdin
中包含的管道数据。
远非优雅,但似乎完成了工作:)
一个简单的例子:
(
( ls -l ~/.bashrcxyz; echo $? ) |
(
piped="$(</dev/stdin)";
[[ "$(tail -n 1 <<<"$piped")" -eq 0 ]] && printf '%s\n' "$piped" | sed '$d' || exit 77
);
echo $?
)
关于什么:
parse_commands /da/cmd/file > >(process_commands)
以上是关于告知左侧故障管道的右侧?的主要内容,如果未能解决你的问题,请参考以下文章