即。在这种情况下,我试图得到" 1"。如果我不使用反引号,我可以通过PIPESTATUS [0]获得,但是当我想保存输出时它似乎不起作用:
## PIPESTATUS[0] works to give me the exit status of 'false':
$ false | true;
$ echo $? ${PIPESTATUS[0]} ${PIPESTATUS[1]};
0 1 0
## doesn't work:
$ a=`false | true`;
$ echo $? ${PIPESTATUS[0]} ${PIPESTATUS[1]};
0 0
更一般地说,我试图完成:将某个程序输出的最后一行保存到变量,但是能够判断程序是否失败:
$ myvar=` ./someprogram | tail -1 `;
$ if [ "what do i put here" ]; then echo "program failed!"; fi
理想情况下,我也想了解发生了什么,而不仅仅是答案是什么。
感谢。
答案 0 :(得分:4)
尝试设置pipefail
选项。它返回失败的管道的最后一个命令。一个例子:
首先我禁用它:
set +o pipefail
创建一个perl
脚本(script.pl
)来测试管道:
#!/usr/bin/env perl
use warnings;
use strict;
if ( @ARGV ) {
die "Line1\nLine2\nLine3\n";
}
else {
print "Line1\nLine2\nLine3\n";
}
在命令行中运行:
myvar=`perl script.pl | tail -1`
echo $? "$myvar"
产量:
0 Line3
看起来是正确的,让我们看一下pipefail
启用:
set -o pipefail
运行命令:
myvar=`perl script.pl fail 2>&1 | tail -1`
echo $? "$myvar"
产量:
255 Line3
答案 1 :(得分:2)
我的解决方案是使用fifos和bash“coproc”内置来获取管道中每个命令的消息和状态。我之前从未使用过fifos。 (哦,小伙,下次我在Fedora上使用BashEclipse)。它变成了管理任何管道命令的通用机制。我解决了这个问题,但不是10或20行代码。更像是一个200 强大的可重复使用的解决方案(花了我三天的时间)。
我分享我的笔记:
* stderr for all pipe commands goes to the fifos.
If you want to get messages from stdout, you must redirect '1>&2', like this:
PIPE_ARRAY=("cat ${IMG}.md5" "cut -f1 -d\" \" 1>&2")
You must put "2>/fifo" first. Otherwise it won\'t work. example:
cat ${IMG}.md5 | cut -f1 -d' ' 1>&2
becomes:
cat ${IMG}.md5 2>/tmp/fifo_s0 | cut -f1 -d" " 2>/tmp/fifo_s1 1>&2 ; PSA=( "${PIPESTATUS[@]}" )
* With more tha one fifo, I found that you must read each fifo in turn.
When "fifo1" gets written to, "fifo0" reads are blocked until you read "fifo1"
I did\'nt use any special tricks like "sleep", "cat", or extra file descriptors
to keep the fifos open.
* PIPESTATUS[@] must be copied to an array immediately after the pipe command returns.
_Any_ reads of PIPESTATUS[@] will erase the contents. Super volatile !
"manage_pipe()" appends '; PSA=( "${PIPESTATUS[@]}" )' to the pipe command string
for this reason. "$?" is the same as the last element of "${PIPESTATUS[@]}",
and reading it seems to destroy "${PIPESTATUS[@]}", but it's not absolutly verifed.
run_pipe_cmd() {
declare -a PIPE_ARRAY MSGS
PIPE_ARRAY=("dd if=${gDEVICE} bs=512 count=63" "md5sum -b >${gBASENAME}.md5")
manage_pipe PIPE_ARRAY[@] "MSGS" # (pass MSGS name, not the array)
}
manage_pipe () {
# input - $1 pipe cmds array, $2 msg retvar name
# output - fifo msg retvar
# create fifos, fifo name array, build cnd string from $1 (re-order redirection if needed)
# run coprocess 'coproc execute_pipe FIFO[@] "$CMDSTR"'
# call 'read_fifos FIFO[@] "M" "S"' (pass names, not arrays for $2 and $3)
# calc last_error, call _error, _errorf
# set msg retvar values (eval ${2}[${i}]='"${Msg[${i}]}"')
}
read_fifos() {
# input - $1 fifo array, $2 msg retvar name, $3 status retvar name
# output - msg, status retvars
# init local fifo_name, pipe_cmd_status, msg arrays
# do read loop until all 'quit' msgs are received
# set msg, status retvar values (i.e. eval ${3}[${i}]='"${Status[${i}]}"'
}
execute_pipe() {
# $1 fifo array, $2 cmdstr, $3 msg retvar, $4 status retvar
# init local fifo_name, pipe_cmd_status arrays
# execute command string, get pipestaus (eval "$_CMDSTR" 1>&2)
# set fifo statuses from copy of PIPESTATUS
# write 'status', 'quit' msgs to fifo
}
答案 2 :(得分:0)
问题是反引号启动了一个子shell。您的子shell有自己的${PIPESTATUS[@]}
数组,但不会持久存储到父shell中。这是将其推送到输出变量$a
然后将其检索到名为${PIPESTATUS2[@]}
的新数组中的技巧:
## PIPESTATUS[0] works to give me the exit status of 'false':
$ false | true
$ echo $? ${PIPESTATUS[0]} ${PIPESTATUS[1]}
0 1 0
## Populate a $PIPESTATUS2 array:
$ a=`false | true; printf :%s "${PIPESTATUS[*]}"`
$ ANS=$?; PIPESTATUS2=(${a##*:})
$ [ -n "${a%:*}" ] && a="${a%:*}" && a="${a%$'\n'}" || a=""
$ echo $ANS ${PIPESTATUS2[0]} ${PIPESTATUS2[1]};
0 1 0
这会将子shell的${PIPESTATUS[@]}
数组保存为$a
末尾的以空格分隔的值列表,然后使用shell变量substring removal将其提取出来(请参阅我给this similar question的更长的例子和描述。只有在您确实要保存$a
的值而没有额外状态时才需要第三行(就像在此示例中它以false | true
运行一样)。