Sample Header Ad - 728x90

eval: $? vs ${PIPESTATUS[@]} (bash)

0 votes
2 answers
566 views
In bash 5.0, I wish to capture the ${PIPESTATUS[@]} of a piped command that is executed via eval. However, eval appears to mask ${PIPESTATUS[@]}, but doesn't mask $? which is the equivalent to ${PIPESTATUS[-1]}. Is there a way to extract ${PIPESTATUS[@]} from the results of eval? Appending something to the commandstring below like && array=( ${PIPESTATUS[@]} ) && export array does not appear to work. Am I correct in assuming from this that $? is not simply ${PIPESTATUS[-1]}? My sample code (ran as root): #!/usr/bin/env bash #without eval, ${PIPESTATUS[@]} has two entries as it should. apt-get install -y java-17-openjdk-amd64 2>&1 | tee -a ~/log commandsPipestatus=( ${PIPESTATUS[@]} ) for status in ${commandsPipestatus[@]}; do echo $status done echo "" echo "" #with eval, ${PIPESTATUS[@]} has one entry and is equal to $? commandstring="apt-get install -y java-17-openjdk-amd64 2>&1 | tee -a ~/log" eval "$commandstring" commandsPipestatus=( ${PIPESTATUS[@]} ) for status in ${commandsPipestatus[@]}; do echo $status done EDIT: Fixed a small technical correction in my original statement about PIPESTATUS. Also, based on the answers below, here are some clarifications: - I'm using eval because I'm building command strings programmatically and some of them may contain bash -c... which run the command as different users. - I looked at setting pipefail and that may work sometimes though not always because sometimes I need to know the status of multiple steps in the pipe. If I could set set -o pipefail and then unset it after that could work, but I'm not seeing how to unset pipefail and I can't simply exit the subshell and then continue in a new one where pipefail is not been set. How do I unset a shell option such as pipefail? - Is my above understanding of how to export an array that contains PIPESTATUS incorrect? How can I simply export PIPESTATUS from the eval subshell? EDIT 2: Thanks to the excellent answer marked below, my final decision was for the case where I am dynamically assembling command strings that include redirection (which is why I need eval), I would use set -o pipefail and then after execution do set +o pipefail. I'm also re-researching best practices for executing dynamically built commands, as I have several years more bash experience since the last time I did. My use cases for eval are: 1. Commands may contain bash -c, so I can't use bash -c to execute them 2. May contain redirection, such as >> log 3. The dynamical commands may also have arguments added to them for debugging purposes As far as I can tell, I may be able to do things differently for 1, and for 3 I should be using things like set -x [command] and trap instead. I have yet to find a solution for 2.
Asked by jitter (5 rep)
Oct 15, 2023, 06:29 AM
Last activity: Oct 23, 2023, 06:47 AM