Save and restore trap state? Easy way to manage multiple handlers for traps?

后端 未结 1 2034
悲&欢浪女
悲&欢浪女 2021-01-31 10:39

What is a good way to override bash trap handlers that don\'t permanently trample existing ones that may or may not already be set? What about dynamically managing

相关标签:
1条回答
  • 2021-01-31 11:22

    Save and Restore Your Trap Handler State in Bash

    I would submit the following stack implementation to track and restore trap state. Using this method, I am able to push trap changes and then pop them away when I'm done with them. This could also be used to chain many trap routines together.

    See the following source file (.trap_stack.sh)

    #!/bin/bash
    trap_stack_name() {
      local sig=${1//[^a-zA-Z0-9]/_}
      echo "__trap_stack_$sig"
    }
    
    extract_trap() {
      echo ${@:3:$(($#-3))}
    }
    
    get_trap() {
      eval echo $(extract_trap `trap -p $1`)
    }
    
    trap_push() {
      local new_trap=$1
      shift
      local sigs=$*
      for sig in $sigs; do
        local stack_name=`trap_stack_name "$sig"`
        local old_trap=$(get_trap $sig)
        eval "${stack_name}"'[${#'"${stack_name}"'[@]}]=$old_trap'
        trap "${new_trap}" "$sig"
      done
    }
    
    trap_pop() {
      local sigs=$*
      for sig in $sigs; do
        local stack_name=`trap_stack_name "$sig"`
        local count; eval 'count=${#'"${stack_name}"'[@]}'
        [[ $count -lt 1 ]] && return 127
        local new_trap
        local ref="${stack_name}"'[${#'"${stack_name}"'[@]}-1]'
        local cmd='new_trap=${'"$ref}"; eval $cmd
        trap "${new_trap}" "$sig"
        eval "unset $ref"
      done
    }
    
    trap_prepend() {
      local new_trap=$1
      shift
      local sigs=$*
      for sig in $sigs; do
        if [[ -z $(get_trap $sig) ]]; then
          trap_push "$new_trap" "$sig"
        else
          trap_push "$new_trap ; $(get_trap $sig)" "$sig"
        fi
      done
    }
    
    trap_append() {
      local new_trap=$1
      shift
      local sigs=$*
      for sig in $sigs; do
        if [[ -z $(get_trap $sig) ]]; then
          trap_push "$new_trap" "$sig"
        else
          trap_push "$(get_trap $sig) ; $new_trap" "$sig"
        fi
      done
    }
    

    This can manage handlers that are defined as named functions and also ad-hoc routines defined like this example trap "kill $!" SIGTERM SIGINT.

    This is the test script I used to help me write it:

    #!/bin/bash
    source .trap_stack.sh
    
    initial_trap='echo "messy" ;'" echo 'handler'"
    non_f_trap='echo "non-function trap"'
    f_trap() {
      echo "function trap"
    }
    
    print_status() {
      echo "    SIGINT  trap: `get_trap SIGINT`"  
      echo "    SIGTERM trap: `get_trap SIGTERM`"
      echo "-------------"
      echo
    }
    
    echo "--- TEST START ---"
    echo "Initial trap state (should be empty):"
    print_status
    
    echo 'Setting messy non-function handler for SIGINT ("original state")'
    trap "$initial_trap" SIGINT
    print_status
    
    echo 'Pop empty stacks (still in original state)'
    trap_pop SIGINT SIGTERM
    print_status
    
    echo 'Push non-function handler for SIGINT'
    trap_push "$non_f_trap" SIGINT
    print_status
    
    echo 'Append function handler for SIGINT and SIGTERM'
    trap_append f_trap SIGINT SIGTERM
    print_status
    
    echo 'Prepend function handler for SIGINT and SIGTERM'
    trap_prepend f_trap SIGINT SIGTERM
    print_status
    
    echo 'Push non-function handler for SIGINT and SIGTERM'
    trap_push "$non_f_trap" SIGINT SIGTERM
    print_status
    
    echo 'Pop both stacks'
    trap_pop SIGINT SIGTERM
    print_status
    
    echo 'Prepend function handler for SIGINT and SIGTERM'
    trap_prepend f_trap SIGINT SIGTERM
    print_status
    
    echo 'Pop both stacks thrice'
    trap_pop SIGINT SIGTERM
    trap_pop SIGINT SIGTERM
    trap_pop SIGINT SIGTERM
    print_status
    
    echo 'Push non-function handler for SIGTERM'
    trap_push "$non_f_trap" SIGTERM
    print_status
    
    echo 'Pop handler state for SIGINT (SIGINT is now back to original state)'
    trap_pop SIGINT
    print_status
    
    echo 'Pop handler state for SIGTERM (SIGTERM is now back to original state)'
    trap_pop SIGTERM
    print_status
    
    0 讨论(0)
提交回复
热议问题