Mario
Mario

Reputation: 1032

trap ERR doesn't work with pipes

I am try to make a system backup script with trap "" ERR. I realized the trap doesn't get called when commands are part of pipes |.

Heres are some parts of my code that don't work with trap "" ERR ...

OpenFiles=$(lsof "$Source" | wc -l)
PackagesList=$(dpkg --get-selections | awk '!/deinstall|purge|hold/ {print $1}' | tee "$FilePackagesList")

How can I get this to work without using if [ "$?" -eq 0 ]; then, or similar coding ? Because this is the reason I declared a trap this way.

Here is the script ...

root@Lian-Li:~# cat /usr/local/bin/create_incremental_backup_of_system.sh 
#!/bin/bash
# Create an incremental GNU-standard backup of important system-files.
# This script works with Debian Jessie and newer systems.
# Created for my lian-li NAS 2016-11-27.

MailTo="[email protected]"  # Mail Address of an admin

Source="boot etc root usr/local usr/lib/cgi-bin var/www"
BackupDirectory=/media/hdd1/backups/lian-li

SubDir="system.d"
FileTimeStamp=$(date "+%Y%m%d%H%M%S")
FileName=$(uname -n)
File="${BackupDirectory}/${SubDir}/${FileName}-${FileTimeStamp}.tgz"
FileIncremental="${BackupDirectory}/${SubDir}/${FileName}.gtar"
FilePackagesList="${BackupDirectory}/${SubDir}/installed_packages_on_${FileName}.txt"

# have2do ...
# Backup rotate

MailContent="None"
TimeStamp=$(date "+%F %T")              # This format "2011-12-31 23:59:59" is needed to read the journal
exec 1> >(logger -i -s -t "$0" -p 3) 2>&1   # all error messages are redirected to syslog journal and after that to stdout
trap "BriefExit" ERR        # Provide information for an admin (via sendmail) when an error occurred and exit the script

function BriefExit(){
    rm -f "$File"
    if [ "$MailContent" = "None" ]
    then
        case "$LANG" in
        de_DE.UTF-8)
            echo "Beende Skript, aufgrund vorherige Fehler." 1>&2
        ;;
        *)
            echo "Stopping script because of previous error(s)." 1>&2
        ;;
        esac
        MailContent=$(journalctl -p 3 -o "short" --since="$TimeStamp" --no-pager)
        ScriptName="${0##*/}"
        SystemName=$(uname -n)
        MailSubject="${SystemName}: ${ScriptName}"
        echo -e "Subject: ${MailSubject}\n\n${MailContent}\n" | sendmail "$MailTo"
    fi
    exit 1
}

if [ ! -d "${BackupDirectory}/${SubDir}" ]
then
    mkdir -p "${BackupDirectory}/${SubDir}"
fi

LoopCount=0
OpenFiles=1
cd /
while [ "$OpenFiles" -ne 0 ]
do
    if [ "$LoopCount" -le 180 ]
    then
        sleep 1
        OpenFiles=$(lsof $Source | wc -l)
        LoopCount=$(($LoopCount + 1))
    else
        echo "Closing Script. Reason: Can't create incremental backup, because some files are open." 1>&2
        BriefExit
    fi
done

tar -cpzf "$File" -g "$FileIncremental" $Source
chmod 0700 "$File"

PackagesList=$(dpkg --get-selections | awk '!/deinstall|purge|hold/ {print $1}' | tee "$FilePackagesList")
while read -r PackageName
do
    case "$PackageName" in
    minidlna)
        # Code ...
    ;;
    slapd)
        # Code ...
    ;;
    esac
done <<< "$PackagesList"

exit 0

Upvotes: 0

Views: 127

Answers (2)

Robert Seaman
Robert Seaman

Reputation: 2592

Another alternative is to look at the status for each stage in the pipeline:

# cat test_bash_return.bash
true | true | false | true
echo "${PIPESTATUS[@]}"

# ./test_bash_return.bash
0 0 1 0

Upvotes: 0

Charles Duffy
Charles Duffy

Reputation: 295649

This isn't a problem with ERR traps at all, or with command substitutions, but with pipelines.

false | true

returns true, unless the pipefail option is set.

Thus in OpenFiles=$(lsof "$Source" | wc -l), only a failure in wc will cause the pipeline to be considered a failure, or in PackagesList=$(dpkg --get-selections | awk '!/deinstall|purge|hold/ {print $1}' | tee "$FilePackagesList"), only a failure in tee will cause the command as a whole to be considered failed.


Put the command set -o pipefail at the top of your script if you want a failure from any pipeline component (as opposed to the last component alone) to cause the command as a whole to be considered failed -- and note the other caveats for ERR traps given in BashFAQ #105.

Upvotes: 3

Related Questions