'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/nullafterhead` 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