同一信号的多个 bash 陷阱

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3338030/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 19:30:53  来源:igfitidea点击:

multiple bash traps for the same signal

bashbash-trap

提问by jes5199

When I use the trapcommand in bash, the previous trapfor the given signal is replaced.

当我trap在 bash 中使用命令时,trap给定信号的前一个被替换。

Is there a way of making more than one trapfire for the same signal?

有没有办法trap为同一个信号发出不止一个信号?

采纳答案by Paused until further notice.

Edit:

编辑:

It appears that I misread the question. The answer is simple:

看来我误读了这个问题。答案很简单:

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

trap handler3 SIGNAL1 SIGNAL2 ...


Original:

原来的:

Just list multiple signals at the end of the command:

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

trap function-name SIGNAL1 SIGNAL2 SIGNAL3 ...

You can find the function associated with a particular signal using trap -p:

您可以使用以下命令找到与特定信号关联的函数trap -p

trap -p SIGINT

Note that it lists each signal separately even if they're handled by the same function.

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

You can add an additional signal given a known one by doing this:

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

eval "$(trap -p SIGUSR1) SIGUSR2"

This works even if there are other additional signals being processed by the same function. In other words, let's say a function was already handling three signals - you could add two more just by referring to one existing one and appending two more (where only one is shown above just inside the closing quotes).

即使同一功能正在处理其他附加信号,这也有效。换句话说,假设一个函数已经处理了三个信号 - 您可以通过引用一个现有信号并附加两个信号(其中仅在上面的右引号内显示​​一个)来再添加两个信号。

If you're using Bash >= 3.2, you can do something like this to extract the function given a signal. Note that it's not completely robust because other single quotes could appear.

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

[[ $(trap -p SIGUSR1) =~ trap\ --\ \'([^7]*)\'.* ]]
function_name=${BASH_REMATCH[1]}

Then you could rebuild your trap command from scratch if you needed to using the function name, etc.

然后,如果您需要使用函数名称等,您可以从头开始重建您的陷阱命令。

回答by Richard Hansen

Technically you can't set multiple traps for the same signal, but you can add to an existing trap:

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

  1. Fetch the existing trap code using trap -p
  2. Add your command, separated by a semicolon or newline
  3. Set the trap to the result of #2
  1. 使用获取现有的陷阱代码 trap -p
  2. 添加您的命令,以分号或换行符分隔
  3. 将陷阱设置为 #2 的结果

Here is a bash function that does the above:

这是一个执行上述操作的 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=; 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' ""; }
            # 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

Example usage:

用法示例:

trap_add 'echo "in trap DEBUG"' DEBUG

回答by Jonathan Leffler

No

About the best you could do is run multiple commands from a single trapfor a given signal, but you cannot have multiple concurrent traps for a single signal. For example:

你能做的最好的事情是从一个trap给定的信号运行多个命令,但你不能为单个信号有多个并发陷阱。例如:

$ trap "rm -f /tmp/xyz; exit 1" 2
$ trap
trap -- 'rm -f /tmp/xyz; exit 1' INT
$ trap 2
$ trap
$

The first line sets a trap on signal 2 (SIGINT). The second line prints the current traps — you would have to capture the standard output from this and parse it for the signal you want. Then, you can add your code to what was already there — noting that the prior code will most probably include an 'exit' operation. The third invocation of trap clears the trap on 2/INT. The last one shows that there are no traps outstanding.

第一行在信号 2 (SIGINT) 上设置陷阱。第二行打印当前陷阱——您必须从中捕获标准输出并解析它以获得您想要的信号。然后,您可以将您的代码添加到已经存在的代码中——注意之前的代码很可能包含一个“退出”操作。第三次调用陷阱清除 2/INT 上的陷阱。最后一个显示没有突出的陷阱。

You can also use trap -p INTor trap -p 2to print the trap for a specific signal.

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

回答by WSimpson

I liked Richard Hansen's answer, but I don't care for embedded functions so an alternate is:

我喜欢 Richard Hansen 的回答,但我不关心嵌入式函数,所以替代方法是:

#===================================================================
# 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=; 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 }'`

        # 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
}

回答by Oscar Byrne

I didn't like having to play with these string manipulations which are confusing at the best of times, so I came up with something like this:

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

(obviously you can modify it for other signals)

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

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

function add_exit_trap {
    local to_add=
    if [[ -z "$exit_trap_command" ]]
    then
        exit_trap_command="$to_add"
    else
        exit_trap_command="$exit_trap_command; $to_add"
    fi
}

回答by Anonymoose

Here's another option:

这是另一种选择:

on_exit_acc () {
    local next=""
    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

Usage:

用法:

$ on_exit date
$ on_exit 'echo "Goodbye from '\''`uname`'\''!"'
$ exit
exit
Sat Jan 18 18:31:49 PST 2014
Goodbye from 'FreeBSD'!
tap# 

回答by Andry

I have been wrote a set of functions for myself to a bit resolve this task in a convenient way.

我已经为自己编写了一组函数,以方便地解决这个任务。

Update: The implementation here is obsoleted and left here as a demonstration. The new implementation is more complex, having dependencies, supports a wider range of cases and quite big to be placed here.

更新:此处的实现已过时,留在这里作为演示。新的实现更加复杂,具有依赖性,支持更广泛的案例,并且非常大,可以放置在这里。

New implementation: https://sf.net/p/tacklelib/tacklelib/HEAD/tree/trunk/bash/tacklelib/traplib.sh

新实现:https: //sf.net/p/tacklelib/tacklelib/HEAD/tree/trunk/bash/tacklelib/traplib.sh

Here is the list of features of the new implementation:

以下是新实现的功能列表:

Pros:

优点

  1. Automatically restores the previous trap handler in nested functions. Originally the RETURNtrap restores ONLY if ALL functions in the stack did set it.
  2. The RETURNsignal trap can support other signal traps to achieve the RAII pattern as in other languages. For example, to temporary disable interruption handling and auto restore it at the end of a function while an initialization code is executing.
  3. Protection from call not from a function context in case of the RETURNsignal trap.
  4. The not RETURNsignal handlers in the whole stack invokes together in a bash process from the bottom to the top and executes them in order reversed to the tkl_push_trapfunction calls
  5. The RETURNsignal trap handlers invokes only for a single function from the bottom to the top in reverse order to the tkl_push_trapfunction calls.
  6. Because the EXITsignal does not trigger the RETURNsignal trap handler, then the EXITsignal trap handler does setup automatically at least once per bash process when the RETURNsignal trap handler makes setup at first time in a bash process. That includes all bash processes, for example, represented as (...)or $(...)operators. So the EXITsignal trap handlers automatically handles all the RETURNtrap handlers before to run itself.
  7. The RETURNsignal trap handler still can call to tkl_push_trapand tkl_pop_trapfunctions to process the not RETURNsignal traps
  8. The RETURNsignal trap handler can call to tkl_set_trap_postponed_exitfunction from both the EXITand RETURNsignal trap handlers. If is called from the RETURNsignal trap handler, then the EXITtrap handler will be called after all the RETURNsignal trap handlers in the bash process. If is called from the EXITsignal trap handler, then the EXITtrap handler will change the exit code after the last EXITsignal trap handler is invoked.
  9. Faster access to trap stack as a global variable instead of usage the (...)or $(...)operators which invokes an external bash process.
  10. The sourcecommand ignores by the RETURNsignal trap handler, so all calls to the sourcecommand will not invoke the RETURNsignal trap user code (marked in the Pros, because RETURNsignal trap handler has to be called only after return from a function in the first place and not from a script inclusion).
  1. 自动恢复嵌套函数中的先前陷阱处理程序。最初RETURN陷阱仅在堆栈中的所有函数都设置它时才恢复。
  2. RETURN信号陷波可以支持其他信号陷阱实现RAII图案作为在其他语言。例如,在执行初始化代码时临时禁用中断处理并在函数结束时自动恢复它。
  3. RETURN信号陷阱的情况下,防止调用而不是来自函数上下文。
  4. RETURN整个堆栈中的 not信号处理程序在 bash 进程中从下到上一起调用,并按照与tkl_push_trap函数调用相反的顺序执行它们
  5. 所述RETURN信号捕获处理程序调用仅针对从底部到以相反的顺序向顶部的单一功能tkl_push_trap的函数调用。
  6. 因为EXIT信号不会触发RETURN信号陷阱处理程序,所以当EXIT信号陷阱处理程序在 bash 进程RETURN中第一次进行设置时,信号陷阱处理程序会在每个 bash 进程中至少自动设置一次。这包括所有 bash 进程,例如,表示为(...)$(...)运算符。所以EXIT信号陷阱处理RETURN程序会在运行之前自动处理所有陷阱处理程序。
  7. RETURN信号陷阱处理程序仍然可以调用tkl_push_traptkl_pop_trap函数来处理没有RETURN信号陷阱
  8. RETURN信号陷阱处理程序可以调用tkl_set_trap_postponed_exit从两个功能EXITRETURN信号陷阱处理程序。如果从RETURN信号陷阱处理程序调用,则EXIT陷阱处理程序将RETURN在 bash 进程中的所有信号陷阱处理程序之后调用 。如果从EXIT信号陷阱处理程序调用,则EXIT陷阱处理程序将在EXIT调用最后一个信号陷阱处理程序后更改退出代码。
  9. 更快地将陷阱堆栈作为全局变量访问,而不是使用调用外部 bash 进程的(...)or$(...)运算符。
  10. source命令被RETURN信号陷阱处理程序忽略,因此对source命令的所有调用都不会调用RETURN信号陷阱用户代码(在 Pros 中标记,因为RETURN信号陷阱处理程序必须首先从函数返回后调用,而不是从脚本包含)。

Cons:

缺点

  1. You must not use builtin trapcommand in the handler passed to the tkl_push_trapfunction as tkl_*_trapfunctions does use it internally.
  2. You must not use builtin exitcommand in the EXITsignal handlers while the EXITsignal trap handler is running. Otherwise that will leave the rest of the RETURNand EXITsignal trap handlers not executed. To change the exit code from the EXIThandler you can use tkl_set_trap_postponed_exitfunction for that.
  3. You must not use builtin returncommand in the RETURNsignal trap handler while the RETURNsignal trap handler is running. Otherwise that will leave the rest of the RETURNand EXITsignal trap handlers not executed.
  4. All calls to the tkl_push_trapand tkl_pop_trapfunctions has no effect if has been called from a trap handler for a signal the trap handler is handling (recursive call through the signal).
  5. You have to replace all builtin trapcommands in nested or 3dparty scripts by tkl_*_trapfunctions if already using the library.
  6. The sourcecommand ignores by the RETURNsignal trap handler, so all calls to the sourcecommand will not invoke the RETURNsignal trap user code (marked in the Cons, because of losing the back compatability here).
  1. 您不能trap在传递给tkl_push_trap函数的处理程序中使用内置命令,因为tkl_*_trap函数确实在内部使用它。
  2. 当信号陷阱处理程序正在运行时,您不得exitEXIT信号处理程序中使用内置命令EXIT。否则,将不执行其余的RETURNEXIT信号陷阱处理程序。要更改EXIT处理程序的退出代码,您可以使用tkl_set_trap_postponed_exit函数。
  3. 当信号陷阱处理程序正在运行时,您不得returnRETURN信号陷阱处理程序中使用内置命令RETURN。否则,其余的RETURNEXIT信号陷阱处理程序将不被执行。
  4. 如果已从陷阱处理程序为陷阱处理程序正在处理的信号(通过信号递归调用)调用tkl_push_traptkl_pop_trap函数,则对和函数的所有调用都无效。
  5. 如果已经使用该库,则trap必须用tkl_*_trap函数替换嵌套或 3dparty 脚本中的所有内置命令。
  6. source命令被RETURN信号陷阱处理程序忽略,因此对source命令的所有调用都不会调用RETURN信号陷阱用户代码(在 Cons 中标记,因为这里失去了向后兼容性)。

Old implementation:

旧实现

traplib.sh

陷阱库

#!/bin/bash

# Script can be ONLY included by "source" command.
if [[ -n "$BASH" && (-z "$BASH_LINENO" || ${BASH_LINENO[0]} -gt 0) ]] && (( ! ${#SOURCE_TRAPLIB_SH} )); then 

SOURCE_TRAPLIB_SH=1 # including guard

function GetTrapCmdLine()
{
  local IFS=$' \t\r\n'
  GetTrapCmdLineImpl RETURN_VALUES "$@"
}

function GetTrapCmdLineImpl()
{
  local out_var=""
  shift

  # drop return values
  eval "$out_var=()"

  local IFS
  local trap_sig
  local stack_var
  local stack_arr
  local trap_cmdline
  local trap_prev_cmdline
  local i

  i=0
  IFS=$' \t\r\n'; for trap_sig in "$@"; do
    stack_var="_traplib_stack_${trap_sig}_cmdline"
    declare -a "stack_arr=(\"${$stack_var[@]}\")"
    if (( ${#stack_arr[@]} )); then
      for trap_cmdline in "${stack_arr[@]}"; do
        declare -a "trap_prev_cmdline=(\"${$out_var[i]}\")"
        if [[ -n "$trap_prev_cmdline" ]]; then
          eval "$out_var[i]=\"$trap_cmdline; $trap_prev_cmdline\"" # the last srored is the first executed
        else
          eval "$out_var[i]=\"$trap_cmdline\""
        fi
      done
    else
      # use the signal current trap command line
      declare -a "trap_cmdline=(`trap -p "$trap_sig"`)"
      eval "$out_var[i]=\"${trap_cmdline[2]}\""
    fi
    (( i++ ))
  done
}

function PushTrap()
{
  # drop return values
  EXIT_CODES=()
  RETURN_VALUES=()

  local cmdline=""
  [[ -z "$cmdline" ]] && return 0 # nothing to push
  shift

  local IFS

  local trap_sig
  local stack_var
  local stack_arr
  local trap_cmdline_size
  local prev_cmdline

  IFS=$' \t\r\n'; for trap_sig in "$@"; do
    stack_var="_traplib_stack_${trap_sig}_cmdline"
    declare -a "stack_arr=(\"${$stack_var[@]}\")"
    trap_cmdline_size=${#stack_arr[@]}
    if (( trap_cmdline_size )); then
      # append to the end is equal to push trap onto stack
      eval "$stack_var[trap_cmdline_size]=\"$cmdline\""
    else
      # first stack element is always the trap current command line if not empty
      declare -a "prev_cmdline=(`trap -p $trap_sig`)"
      if (( ${#prev_cmdline[2]} )); then
        eval "$stack_var=(\"${prev_cmdline[2]}\" \"$cmdline\")"
      else
        eval "$stack_var=(\"$cmdline\")"
      fi
    fi
    # update the signal trap command line
    GetTrapCmdLine "$trap_sig"
    trap "${RETURN_VALUES[0]}" "$trap_sig"
    EXIT_CODES[i++]=$?
  done
}

function PopTrap()
{
  # drop return values
  EXIT_CODES=()
  RETURN_VALUES=()

  local IFS

  local trap_sig
  local stack_var
  local stack_arr
  local trap_cmdline_size
  local trap_cmd_line
  local i

  i=0
  IFS=$' \t\r\n'; for trap_sig in "$@"; do
    stack_var="_traplib_stack_${trap_sig}_cmdline"
    declare -a "stack_arr=(\"${$stack_var[@]}\")"
    trap_cmdline_size=${#stack_arr[@]}
    if (( trap_cmdline_size )); then
      (( trap_cmdline_size-- ))
      RETURN_VALUES[i]="${stack_arr[trap_cmdline_size]}"
      # unset the end
      unset $stack_var[trap_cmdline_size]
      (( !trap_cmdline_size )) && unset $stack_var

      # update the signal trap command line
      if (( trap_cmdline_size )); then
        GetTrapCmdLineImpl trap_cmd_line "$trap_sig"
        trap "${trap_cmd_line[0]}" "$trap_sig"
      else
        trap "" "$trap_sig" # just clear the trap
      fi
      EXIT_CODES[i]=$?
    else
      # nothing to pop
      RETURN_VALUES[i]=""
    fi
    (( i++ ))
  done
}

function PopExecTrap()
{
  # drop exit codes
  EXIT_CODES=()

  local IFS=$' \t\r\n'

  PopTrap "$@"

  local cmdline
  local i

  i=0
  IFS=$' \t\r\n'; for cmdline in "${RETURN_VALUES[@]}"; do
    # execute as function and store exit code
    eval "function _traplib_immediate_handler() { $cmdline; }"
    _traplib_immediate_handler
    EXIT_CODES[i++]=$?
    unset _traplib_immediate_handler
  done
}

fi

test.sh

测试文件

#/bin/bash

source ./traplib.sh

function Exit()
{
  echo exitting...
  exit $@
}

pushd ".." && {
  PushTrap "echo popd; popd" EXIT
  echo 111 || Exit
  PopExecTrap EXIT
}

GetTrapCmdLine EXIT
echo -${RETURN_VALUES[@]}-

pushd ".." && {
  PushTrap "echo popd; popd" EXIT
  echo 222 && Exit
  PopExecTrap EXIT
}

Usage

用法

cd ~/test
./test.sh

Output

输出

~ ~/test
111
popd
~/test
--
~ ~/test
222
exitting...
popd
~/test

回答by Daniel C. Sobral

There's no way to have multiple handlers for the same trap, but the same handler can do multiple things.

没有办法为同一个陷阱设置多个处理程序,但同一个处理程序可以做多种事情。

The one thing I don't like in the various other answers doing the same thing is the use of string manipulation to get at the current trap function. There are two easy ways of doing this: arrays and arguments. Arguments is the most reliable one, but I'll show arrays first.

在做同样事情的各种其他答案中,我不喜欢的一件事是使用字符串操作来获取当前的陷阱函数。有两种简单的方法可以做到这一点:数组和参数。参数是最可靠的,但我会先展示数组。

Arrays

数组

When using arrays, you rely on the fact that trap -p SIGNALreturns trap -- ??? SIGNAL, so whatever is the value of ???, there are three more words in the array.

当使用数组,你依赖于一个事实,即trap -p SIGNAL回报trap -- ??? SIGNAL,所以无论是价值???,还有数组中的三个词。

Therefore you can do this:

因此,您可以这样做:

declare -a trapDecl
trapDecl=($(trap -p SIGNAL))
currentHandler="${trapDecl[@]:2:${#trapDecl[@]} - 3}"
eval "trap -- 'your handler;'${currentHandler} SIGNAL"

So let's explain this. First, variable trapDeclis declared as an array. If you do this inside a function, it will also be local, which is convenient.

所以让我们解释一下。首先,变量trapDecl被声明为一个数组。如果在函数内部这样做,它也会是本地的,这很方便。

Next we assign the output of trap -p SIGNALto the array. To give an example, let's say you are running this after having sourced osht (unit testing for shell), and that the signal is EXIT. The output of trap -p EXITwill be trap -- '_osht_cleanup' EXIT, so the trapDeclassignment will be substituted like this:

接下来,我们将 的输出分配给trap -p SIGNAL数组。举个例子,假设您在获取 osht(shell 单元测试)后运行它,并且信号是EXIT. 的输出trap -p EXIT将是trap -- '_osht_cleanup' EXIT,因此trapDecl分配将被替换如下:

trapDecl=(trap -- '_osht_cleanup' EXIT)

The parenthesis there are normal array assignment, so trapDeclbecomes an array with four elements: trap, --, '_osht_cleanup'and EXIT.

括号里面是普通的数组赋值,所以trapDecl变成了一个有四个元素的数组:trap, --,'_osht_cleanup'EXIT

Next we extract the current handler -- that could be inlined in the next line, but for explanation's sake I assigned it to a variable first. Simplifying that line, I'm doing this: currentHandler="${array[@]:offset:length}", which is the syntax used by Bash to say pick lengthelements starting at element offset. Since it starts counting from 0, number 2will be '_osht_cleanup'. Next, ${#trapDecl[@]}is the number of elements inside trapDecl, which will be 4 in the example. You subtract 3 because there are three elements you don't want: trap, --and EXIT. I don't need to use $(...)around that expression because arithmetic expansion is already performed on the offsetand lengtharguments.

接下来我们提取当前处理程序——可以在下一行内联,但为了解释起见,我首先将它分配给一个变量。简化该行,我正在这样做:currentHandler="${array[@]:offset:length}",这是 Bash 使用的语法,表示length从 element 开始选择元素offset。由于它从 开始计数0,因此数字2将为'_osht_cleanup'。接下来${#trapDecl[@]}是 内部的元素数,trapDecl在示例中为 4。您减去 3,因为有三个您不想要的元素:trap,--EXIT。我不需要$(...)在该表达式周围使用,因为已经对offsetlength参数执行了算术扩展。

The final line performs an eval, which is used so that the shell will interpret the quoting from the output of trap. If we do parameter substitution on that line, it expands to the following in the example:

最后一行执行 an eval,使用它是为了让 shell 解释从 的输出中引用的内容trap。如果我们在该行进行参数替换,它会在示例中扩展为以下内容:

eval "trap -- 'your handler;''_osht_cleanup' EXIT"

Do not be confused by the double quote in the middle (''). Bash simply concatenates two quotes strings if they are next to each other. For example, '1'"2"'3''4'is expanded to 1234by Bash. Or, to give a more interesting example, 1" "2is the same thing as "1 2". So eval takes that string and evaluates it, which is equivalent to executing this:

不要被中间的双引号 ( '')弄糊涂了。如果两个引号字符串彼此相邻,则 Bash 只是将它们连接起来。例如,'1'"2"'3''4'1234Bash扩展为。或者,举一个更有趣的例子,1" "2"1 2". 因此 eval 获取该字符串并对其进行评估,这相当于执行以下操作:

trap -- 'your handler;''_osht_cleanup' EXIT

And that will handle the quoting correctly, turning everything between --and EXITinto a single parameter.

这将正确处理引用,将--和之间的所有内容EXIT转换为单个参数。

To give a more complex example, I'm prepending a directory clean up to the osht handler, so my EXITsignal now has this:

举一个更复杂的例子,我在 osht 处理程序之前准备了一个目录清理,所以我的EXIT信号现在有这个:

trap -- 'rm -fr '\''/var/folders/7d/qthcbjz950775d6vn927lxwh0000gn/T/tmp.CmOubiwq'\'';_osht_cleanup' EXIT

If you assign that to trapDecl, it will have size 6 because of the spaces on the handler. That is, 'rmis one element, and so is -fr, instead of 'rm -fr ...'being a single element.

如果将其分配给trapDecl,则由于处理程序上的空格,它将具有大小 6。也就是说,'rm是一个元素, 也是-fr,而不是'rm -fr ...'单个元素。

But currentHandlerwill get all three elements (6 - 3 = 3), and the quoting will work out when evalis run.

但是currentHandler将获得所有三个元素 (6 - 3 = 3),并且引用将在eval运行时计算出来。

Arguments

参数

Arguments just skips all the array handling part and uses evalup front to get the quoting right. The downside is that you replace the positional arguments on bash, so this is best done from a function. This is the code, though:

参数只是跳过所有数组处理部分,并预先使用eval以获得正确的引用。缺点是您在 bash 上替换了位置参数,因此最好从函数中完成。不过,这是代码:

eval "set -- $(trap -p SIGNAL)"
trap -- "your handler${3:+;}" SIGNAL

The first line will set the positional arguments to the output of trap -p SIGNAL. Using the example from the Arrays section, $1will be trap, $2will be --, $3will be _osht_cleanup(no quotes!), and $4will be EXIT.

第一行将位置参数设置为 的输出trap -p SIGNAL。使用数组部分中的示例,$1将是trap$2将是--$3将是_osht_cleanup(没有引号!)和$4将是EXIT

The next line is pretty straightforward, except for ${3:+;}. The ${X:+Y}syntax means "output Yif the variable Xis unset or null". So it expands to ;if $3is set, or nothing otherwise (if there was no previous handler for SIGNAL).

下一行非常简单,除了${3:+;}. 该${X:+Y}语法是指“输出Y如果变量X是设置或者为空”。所以它扩展为;if$3已设置,或者没有其他情况(如果没有先前的处理程序SIGNAL)。

回答by kdb

I add a slightly more robust version of Laurent Simon's trap-addscript:

我想补充的一个稍微更强大的版本洛朗西蒙trap-add脚本:

  • Allows using arbitrary commands as trap, including such with 'characters
  • Works only in bash; It could be rewritten with sedinstead of bash pattern substitution, but that would make it significantly slower.
  • Still suffers from causing unwanted inheritance of the traps in subshells.
  • 允许使用任意命令作为陷阱,包括带有'字符的命令
  • 仅适用于 bash;它可以用sed替换 bash 模式替换来重写,但这会使其显着变慢。
  • 仍然会导致子壳中陷阱的不必要继承。

trap-add () {
    local handler=$(trap -p "")
    handler=${handler/trap -- \'/}    # /- Strip `trap '...' SIGNAL` -> ...
    handler=${handler%\'*}            # \-
    handler=${handler//\'\\'\'/\'}   # <- Unquote quoted quotes ('\'')
    trap "${handler} ;" ""
}

回答by Laurent Simon

Simple ways to do it

简单的方法来做到这一点

  1. If all the signal handling functions are know at the same time, then the following is sufficient (has said by Jonathan):
  1. 如果同时知道所有信号处理函数,那么以下就足够了(Jonathan说):
trap 'handler1;handler2;handler3' EXIT
  1. Else, if there is existing handler(s) that should stay, then new handlers can easily be added like this:
  1. 否则,如果存在应保留的现有处理程序,则可以像这样轻松添加新处理程序:
trap "$( trap -p EXIT | cut -f2 -d \' );newHandler" EXIT
  1. If you don't know if there is existing handlers but want to keep them in this case, do the following:
  1. 如果您不知道是否存在现有处理程序但希望在这种情况下保留它们,请执行以下操作:
 handlers="$( trap -p EXIT | cut -f2 -d \' )"
 trap "${handlers}${handlers:+;}newHandler" EXIT
  1. It can be factorized in a function like that:
  1. 它可以分解为这样的函数:
trap-add() {
    local sig="${2:?Signal required}"
    hdls="$( trap -p ${sig} | cut -f2 -d \' )";
    trap "${hdls}${hdls:+;}${1:?Handler required}" "${sig}"
}

export -f trap-add

Usage:

用法:

trap-add 'echo "Bye bye"' EXIT
trap-add 'echo "See you next time"' EXIT

Remark: This works only has long as the handlers are function names, or simple instructions that did not contains any simple cote (simple cotes conflicts with cut -f2 -d \').

备注:这仅适用于处理程序是函数名称或不包含任何简单 cote 的简单指令(simple cotes 与 冲突cut -f2 -d \')。