陷阱ERR不适用于管道

时间:2017-04-19 14:26:42

标签: bash

我尝试使用trap "" ERR制作系统备份脚本。我意识到当命令是管道|的一部分时,不会调用陷阱。

Heres是我的代码的某些部分,不适用于trap "" ERR ...

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

如何在不使用if [ "$?" -eq 0 ]; then或类似编码的情况下实现此功能?因为这就是我用这种方式宣布陷阱的原因。

这是脚本......

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="admin@example.com"  # 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

2 个答案:

答案 0 :(得分:3)

这根本不是ERR陷阱的问题,有命令替换,但有管道。

false | true

返回true,除非设置了pipefail选项。

因此,在OpenFiles=$(lsof "$Source" | wc -l)中,只有wc中的失败才会导致管道被视为失败,或者PackagesList=$(dpkg --get-selections | awk '!/deinstall|purge|hold/ {print $1}' | tee "$FilePackagesList")中只有tee中的失败才会导致整个命令被认为是失败的。

如果您希望任何管道组件发生故障(而不是单独的最后一个组件)导致整个命令被视为失败,请将命令set -o pipefail放在脚本的顶部 - 并注意BashFAQ #105中给出的ERR陷阱的其他注意事项。

答案 1 :(得分:0)

另一种方法是查看管道中每个阶段的状态:

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

# ./test_bash_return.bash
0 0 1 0