4

I wrote a simple script

 #!/bin/bash -x
 selentry=`ls -l / | sort ; ts=${PIPESTATUS[0]}`
 echo $ts

but $ts show nothing. How I can display $ts variable, or how I can get exit status code from command in variable?

selentry=`ls -l / | sort`
  • 8
    ts is not being set because it runs in a subshell. Also, use $(...) rather than backticks. It's easier on the eyes and nests better. – Kusalananda Jan 25 '17 at 13:44
4

In:

selentry=`ls -l / | sort ; ts=${PIPESTATUS[0]}`

same as the more modern:

selentry=$(ls -l / | sort ; ts=${PIPESTATUS[0]})

The code inside the $(...) is run in a subshell environment (in the case of bash and shells other than ksh93, it's even run in a different shell process). So any modification made to variables in that subshell won't affect the parent shell.

When you do:

var=$(cmd)

The exit status however of cmd is made available in $?. That doesn't extend to $PIPESTATUS which in the case of:

var=$(foo | bar)

contains just one value (the exit code of the subshell which here would be the exit status of bar (unless the pipefail option is on in which case it may be the exit status of foo if it's non-zero). It's even worse in zsh where the $pipestatus array is not affected by assignment commands.

Here, though, if you don't care about the exit status of sort (of the subshell), you could do:

selentry=$(ls -l / | sort; exit "${PIPESTATUS[0]}")
ts=$?

Here, you could also do:

exec 3< <(ls -l /) # here ls is started as an asynchronous command
ls_pid=$!
selentry=$(sort <&3)
sort_status=$?
wait "$ls_pid"
exec 3<&- # close that fd 3
ls_status=$?

Or even:

{
  selentry=$(sort)
  sort_status=$?
  wait "$!"
  ls_status=$?
} < <(ls -l /)

On the more general question of having variable assignments survive command substitutions, in ksh93, you can use the ${ cmd;} form of command substitution (ksh93 doesn't support $PIPESTATUS/$pipestatus though).

var=${
  foo; c1=$?
  bar; c2=$?
}

There's no equivalent in other Bourne-like shells, you'd need to pass the data via some other mean like a temporary file:

var=$(
  foo; echo "c1=$?" > "$tempfile"
  bar; echo "c2=$?" >> "$tempfile"
)
. "$tempfile"

Or here:

selentry=$(
  ls -l / | sort
  typeset -p PIPESTATUS | sed '1s/PIPESTATUS/my_&/' > "$tempfile"
}
. "$tempfile"
ls_status=${my_PIPESTATUS[0]}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.