bash陷阱在脚本源时不工作

  • 本文关键字:工作 脚本 陷阱 bash bash
  • 更新时间 :
  • 英文 :


对不起,我不知道下面是什么问题。当我在脚本中按ctrl-c时,陷阱会起作用,但当脚本是源执行时,陷阱不会起作用。

(test2.sh)例子:

#!/bin/bash
ctrl_c()
{                                                 
echo "user canceled."                                    
}                                                          
trap ctrl_c INT
sleep 500
trap - INT

发生以下情况:

[root@localhost ~]# ./test2
^Cuser canceled.
[root@localhost ~]# . test2
^C

这是RHEL 7, Bash 4.2,但同样的问题也发生在其他Linux发行版和Bash 4.4。

请帮助。谢谢!

我似乎记得交互式shell(这是您正在做陷阱的地方,因为您正在采购它而不是异步运行它)对SIGINT有特殊处理。它们自己捕获它,以便进程控制调用可以被中断。

我怀疑那妨碍了你想做的事。


更新:刚刚看了一下bash的手册页,SIGNALS部分有一点模糊,但它确实似乎证实了我对交互式shell中特殊处理的回忆。

信号

bash是交互式的,在没有任何陷阱的情况下,它忽略SIGTERM(这样kill 0不会杀死交互式shell),并且捕获和处理SIGINT(这样wait内置是可中断的)。在所有情况下,bash忽略SIGQUIT

我是这样解决我的问题的。也许这不是最好或最优雅的方式,但它似乎工作得很好。

诀窍是在后台任务(子进程)中运行该过程,然后等待该进程完成。当后台进程($!)运行时,trap INT将工作并进行清理。由于我需要源一些命令,我将它们写入文件,然后在主脚本中源执行这些文件。但是,按Ctrl-C键将清理这些文件,因此脚本结束。 例如:

# scan.sh
scan.sh, not sourced. Aborting...
# source scan.sh
[|] Working...^C
scan.sh: waiting for process 22390 to terminate...
scan.sh: user canceled.
[1]+  Interrupt               doit $f_menu $f_alias

我没有找到一个有效的解决方案来消除& Interrupted"消息,但这不是一个真正的问题。

#!/bin/bash
....
# Preliminary options and environment. 
save_monitor=$(set +o | grep -w monitor)
set +o monitor # Turn off monitor mode.
save_history=$(set +o | grep -w history)
set +o history # Turn off command line history.
save_extglob=$(shopt -p extglob)
shopt -s extglob
f_menu=/tmp/1_$$
f_alias=/tmp/2_$$ 
iam=scan.sh                                        
cleanup()
{ # Requires $1 (f_menu) $2 (f_alias) args.
# Restore shell the previous shell environment.  
unset i f_pid f_menu f_alias aver iam list aname line sourced
unset spinner doit cleanup ctrl_c
trap - INT
rm -f $1 $2 # Don't use rm alias.
eval "$save_extglob"
eval "$save_monitor"
eval "$save_history"
unset save_extglob save_history save_monitor
}

# Exit if the script was not sourced.
$(return >/dev/null 2>&1) && sourced=1 || unset sourced
[[ "$sourced" ]] || { echo; echo "$iam, not sourced. Aborting..."; exit 1; } 
ctrl_c()
{ # Requried: f_pid f_quit
# Optional: f_menu $f_alias
kill -TERM $f_pid
while (( f_pid )); do
echo; echo "$iam: waiting for process $f_pid to terminate..."
sleep 2
[[ $(kill -0 $f_pid 2>&-) ]] || f_pid=
done
echo "$iam: user canceled."
cleanup $f_menu $f_alias
}
trap ctrl_c INT
spinner()
{ 
# Optional: $1
local disp
disp='|/-'
while kill -0 $! 2>&-; do
printf " [%c] %s..." "$disp" "$1"
disp=${disp#?}${disp%%???}
sleep .1
printf "r"
done
}
doit()
{ 
# Requires $1 (f_menu) $2 (f_alias) args. 

.....
}
echo
doit $f_menu $f_alias &
f_pid=$!; spinner "Working"; wait $f_pid
# Following will no longer work after user hit control-c.
source $f_alias 2>&-
cleanup $f_menu $f_alias 2>&-
# end