2

I composed a piped command and tried to run it under timeout. In case the last piped command succeeds but the first does not, the status is 0 (since the last command succeed). I read this post about getting the status using PIPESTATUS: Pipe output and capture exit status in Bash (It also suggests using set -o pipefail but it also does not work). However, when I try to run it under timeout the PIPESTATUS[0] is incorrect, this might be due to the fact I need to run the piped command using bash -c when running it under timeout (the next post explains why timeout <time> bash -c <command> syntax should be used Why does `timeout` not work with pipes?)

A code example:

function func {
    HELLO="hello"
    DOES_NOT_EXIST="does_not_exist.txt"
    CMD="ls $DOES_NOT_EXIST | echo $HELLO"
    echo $CMD
    timeout 5 bash -c "$CMD"
    echo ${PIPESTATUS[0]}
}

#main
func

output:

ls does_not_exist.txt | echo hello
hello
ls: cannot access does_not_exist.txt: No such file or directory
0  

The last line is the status of echo hello and not ls's failure status (should be 2 if I am not mistaken). Is there a way to get the status of the first command in the pipe? (My actual need is to get the status of the first piped command no matter whether it failed or not)

user5721565
  • 134
  • 1
  • 9
  • 3
    `$PIPESTATUS` only gets the status of commands run by the current shell. You're running the commands in a different `bash` process with `bash -c` – Barmar Aug 13 '21 at 19:27

2 Answers2

1

I tried it under bash command line and it seems to work fine:

$ { echo foo; exit 2; } | grep foo
foo
$ echo ${PIPESTATUS[0]} ${PIPESTATUS[1]}
2 0
$ { echo foo; exit 18; } | grep bar
$ echo ${PIPESTATUS[0]} ${PIPESTATUS[1]}
18 1
$ timeout 5 bash -c '{ echo foo; exit 18; } | grep bar; echo ${PIPESTATUS[0]} ${PIPESTATUS[1]}'
18 1
$ timeout 5 bash -c '{ echo foo; exit 18; } | grep foo; echo ${PIPESTATUS[0]} ${PIPESTATUS[1]}'
foo
18 0
Rachid K.
  • 4,490
  • 3
  • 11
  • 30
  • 1
    Try making your script `timeout bash -c '{ sleep 6;echo foo; exit 18; } | grep bar; echo "${PIPESTATUS[@]}"` to demonstrate a case that isn't as-yet successfully handled. – Charles Duffy Aug 13 '21 at 19:45
1

timeout 5 bash -c "$CMD" runs the pipe in another process. A child process cannot alter the variables of its parent, hence you cannot use PIPESTATUS outside of bash -c.

To run the pipe in the same process, you can prefix each command in the pipe by timeout:

timeout 5 firstCmd | timeout 5 secondCmd
declare -p PIPESTATUS
Socowi
  • 25,550
  • 3
  • 32
  • 54