对不起,我不知道下面是什么问题。当我在脚本中按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