'ignoring specific exit code in bash
Note: Question does not duplicate Ignoring specific errors in a shell script .
Suppose it is needed to capture the leading characters of a encoded representation of a file.
In shell (tested in Bash), it is easy to use the following form:
encoded="$(< file base64 | head -c16)"
The statement functions desired, except under certain alterations to the environment.
Consider the following:
set -o errexit -o pipefail
shopt -s inherit_errexit
encoded="$(< file base64 | head -c16)"
The final line would cause termination of a script, because of the non-zero return status (141) given by base64
, unhappy with closed pipe. The return status is propagated to the pipe and then to the invoking shell.
The undesired effect requires a workaround, such as follows:
set -o errexit -o pipefail
shopt -s inherit_errexit
encoded="$((< file base64 || :) | head -c16)"
The :
has the same effect as would have the keyword true
, to evaluate as a non-error.
However, this approach leads to a further unwanted effect.
The following shows a variation with a different error:
set -o errexit -o pipefail
shopt -s inherit_errexit
encoded="$((< /not/a/real/file base64 || :) | head -c16)"
echo $?
The printed code is zero. Now, a true error has been masked.
The most obvious solution, as follows, is rather verbose
set -o errexit -o pipefail
shopt -s inherit_errexit
encoded="$((< /not/a/real/file base64 || [ $? == 141 ]) | head -c16)"
echo $?
Is a more compact form available? Is any environment alteration available such that statements masks only the particular status code, without the explicit inline expression?
Solution 1:[1]
First off, apparently, to actually provoke the 141 error the file needs to be fairly, large, e.g.
head -c 1000000 /dev/urandom > file
now, as you said, this script sh.sh
will terminate before showing encoded: ...
:
#!/bin/bash
set -o errexit -o pipefail
shopt -s inherit_errexit
encoded="$(< file base64 | head -c16)"
echo "encoded: $encoded"
instead of checking for the error code, you could let base64 continue to pipe the rest of its data to /dev/null
by invoking ^cat > /dev/nullafter
head` is done:
#!/bin/bash
set -o errexit -o pipefail
shopt -s inherit_errexit
encoded="$(< file base64 | ( head -c16 ; cat > /dev/null ) )"
echo "encoded: $encoded"
now you will get encoded: NvyX2Zx4nTDjtQO8
or whatever.
And this does not mask other errors like the file not existing:
$ ./sh.sh
./sh.sh: line 5: file: No such file or directory
However, it will be less efficient because the whole file will be read.
Solution 2:[2]
For your specific example program, you could also just change your approach. 16 base 64 characters represent 16 * 6 = 96 bits, so 96/8 = 12 bytes of data from the file are needed:
encoded="$(head -c12 file | base64)"
this will not cause SIGPIPE.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | masterxilo |
Solution 2 | masterxilo |