多个bash陷阱用于相同的信号

jes*_*199 54 bash bash-trap

当我在bash中使用"trap"命令时,替换给定信号的先前陷阱.

是否有办法为同一信号制造多个陷阱?

Ric*_*sen 45

从技术上讲,您不能为同一信号设置多个陷阱,但您可以添加到现有陷阱:

  1. 使用获取现有陷阱代码 trap -p
  2. 添加命令,用分号或换行符分隔
  3. 将陷阱设置为#2的结果

这是一个执行上述操作的bash函数:

# note: printf is used instead of echo to avoid backslash
# processing and to properly handle values that begin with a '-'.

log() { printf '%s\n' "$*"; }
error() { log "ERROR: $*" >&2; }
fatal() { error "$@"; exit 1; }

# appends a command to a trap
#
# - 1st arg:  code to add
# - remaining args:  names of traps to modify
#
trap_add() {
    trap_add_cmd=$1; shift || fatal "${FUNCNAME} usage error"
    for trap_add_name in "$@"; do
        trap -- "$(
            # helper fn to get existing trap command from output
            # of trap -p
            extract_trap_cmd() { printf '%s\n' "$3"; }
            # print existing trap command with newline
            eval "extract_trap_cmd $(trap -p "${trap_add_name}")"
            # print the new trap command
            printf '%s\n' "${trap_add_cmd}"
        )" "${trap_add_name}" \
            || fatal "unable to add to trap ${trap_add_name}"
    done
}
# set the trace attribute for the above function.  this is
# required to modify DEBUG or RETURN traps because functions don't
# inherit them unless the trace attribute is set
declare -f -t trap_add
Run Code Online (Sandbox Code Playgroud)

用法示例:

trap_add 'echo "in trap DEBUG"' DEBUG
Run Code Online (Sandbox Code Playgroud)

  • 效果很好(只要陷阱最初不为空) (2认同)

Pau*_*ce. 29

编辑:

看来我误解了这个问题.答案很简单:

handler1 () { do_something; }
handler2 () { do_something_else; }
handler3 () { handler1; handler2; }

trap handler3 SIGNAL1 SIGNAL2 ...
Run Code Online (Sandbox Code Playgroud)

原版的:

只需在命令末尾列出多个信号:

trap function-name SIGNAL1 SIGNAL2 SIGNAL3 ...
Run Code Online (Sandbox Code Playgroud)

您可以使用以下方法找到与特定信号关联的功能trap -p:

trap -p SIGINT
Run Code Online (Sandbox Code Playgroud)

请注意,即使它们由相同的功能处理,它也会单独列出每个信号.

通过执行以下操作,您可以添加已知的额外信号:

eval "$(trap -p SIGUSR1) SIGUSR2"
Run Code Online (Sandbox Code Playgroud)

即使有相同功能处理其他附加信号,这也可以工作.换句话说,假设一个函数已经处理了三个信号 - 你可以通过引用一个现有函数再添加两个信号,再添加两个(其中只有一个在结束引号内部显示).

如果你正在使用Bash> = 3.2,你可以做这样的事情来提取给定信号的函数.请注意,它不是完全健壮的,因为可能会出现其他单引号.

[[ $(trap -p SIGUSR1) =~ trap\ --\ \'([^\047]*)\'.* ]]
function_name=${BASH_REMATCH[1]}
Run Code Online (Sandbox Code Playgroud)

然后,如果需要使用函数名等,则可以从头开始重建trap命令.

  • 好的 - 我们得出结论,问题是要求两个不同的东西.您假设问题是"您可以在陷阱处理程序中执行任意复杂的操作",答案当然是肯定的 - 您可以在陷阱处理程序中编写任何shell脚本命令.我假设问题是"我可以有两个独立的陷阱同时活动:'陷阱'xyz"2`'和'陷阱'pqr"2`'同时,答案是(正如我所说)鉴于对问题的解释存在差异,答案的差异并不令人意外. (6认同)
  • 你仍然只是发射一个陷阱 - 有问题的陷阱调用多个功能并且实现效果或多或少是无可争辩的.认为可能存在任何争议理由的唯一理由是,如果首先安装handler1,并且设计为退出,则不会触发handler2.但仍然只有一个行动被解雇了.(你总是能够在触发动作中有一个任意复杂的操作序列.) (4认同)
  • 他要求对同一个信号使用多个陷阱,而不是对多个信号使用同一个陷阱。 (2认同)

Jon*_*ler 11

没有

关于您可以做的最好的事情是从trap给定信号的单个命令运行多个命令,但是单个信号不能有多个并发陷阱.例如:

$ trap "rm -f /tmp/xyz; exit 1" 2
$ trap
trap -- 'rm -f /tmp/xyz; exit 1' INT
$ trap 2
$ trap
$
Run Code Online (Sandbox Code Playgroud)

第一行在信号2(SIGINT)上设置陷阱.第二行打印当前陷阱 - 您必须从此捕获标准输出并解析它以获得所需信号.然后,您可以将代码添加到已存在的代码中 - 注意先前的代码很可能包含"退出"操作.第三次调用trap会清除2/INT上的陷阱.最后一个显示没有未完成的陷阱.

您还可以使用trap -p INTtrap -p 2打印特定信号的陷阱.


WSi*_*son 6

我喜欢理查德汉森的答案,但我不关心嵌入式功能,所以替代方案是:

#===================================================================
# FUNCTION trap_add ()
#
# Purpose:  appends a command to a trap
#
# - 1st arg:  code to add
# - remaining args:  names of traps to modify
#
# Example:  trap_add 'echo "in trap DEBUG"' DEBUG
#
# See: http://stackoverflow.com/questions/3338030/multiple-bash-traps-for-the-same-signal
#===================================================================
trap_add() {
    trap_add_cmd=$1; shift || fatal "${FUNCNAME} usage error"
    new_cmd=
    for trap_add_name in "$@"; do
        # Grab the currently defined trap commands for this trap
        existing_cmd=`trap -p "${trap_add_name}" |  awk -F"'" '{print $2}'`

        # Define default command
        [ -z "${existing_cmd}" ] && existing_cmd="echo exiting @ `date`"

        # Generate the new command
        new_cmd="${existing_cmd};${trap_add_cmd}"

        # Assign the test
         trap   "${new_cmd}" "${trap_add_name}" || \
                fatal "unable to add to trap ${trap_add_name}"
    done
}
Run Code Online (Sandbox Code Playgroud)

  • 如果现有的 trap 操作包含单引号(例如 `trap "echo 'foo'" EXIT`),这将不起作用,这就是我的答案使用嵌入函数的原因。 (5认同)

Ano*_*ose 5

这是另一种选择:

on_exit_acc () {
    local next="$1"
    eval "on_exit () {
        local oldcmd='$(echo "$next" | sed -e s/\'/\'\\\\\'\'/g)'
        local newcmd=\"\$oldcmd; \$1\"
        trap -- \"\$newcmd\" 0
        on_exit_acc \"\$newcmd\"
    }"
}
on_exit_acc true
Run Code Online (Sandbox Code Playgroud)

用法:

on_exit_acc () {
    local next="$1"
    eval "on_exit () {
        local oldcmd='$(echo "$next" | sed -e s/\'/\'\\\\\'\'/g)'
        local newcmd=\"\$oldcmd; \$1\"
        trap -- \"\$newcmd\" 0
        on_exit_acc \"\$newcmd\"
    }"
}
on_exit_acc true
Run Code Online (Sandbox Code Playgroud)


Osc*_*rne 5

我不喜欢玩这些在最好的时候令人困惑的字符串操作,所以我想出了这样的东西:

(显然您可以针对其他信号对其进行修改)

exit_trap_command=""
function cleanup {
    eval "$exit_trap_command"
}
trap cleanup EXIT

function add_exit_trap {
    local to_add=$1
    if [[ -z "$exit_trap_command" ]]
    then
        exit_trap_command="$to_add"
    else
        exit_trap_command="$exit_trap_command; $to_add"
    fi
}
Run Code Online (Sandbox Code Playgroud)