bash SIGINT 陷阱触发一次但不会再次触发
bash SIGINT trap fires once but never again
我需要帮助来理解 SIGINT 是如何在 shell 脚本的主要流程和该脚本中的函数调用之间传播的。对于我的脚本,我有一个主菜单,它接受用户输入并根据该输入调用子函数。子函数与用户交互。
我的目标如下:
- 我需要子函数能够访问主脚本中的函数定义和变量,因此子shell不是一个好的选择
- 子函数应该在它自己的文件中,否则我的主要 shell 脚本会太大而且不实用
- 我想在子函数中使用 ctrl+c return 用户进入主菜单
- 我想在主菜单中按 ctrl+c 理想地打印一些东西,如果再次完成,则退出。如果它必须第一次退出是可以接受的
我看到的行为是,我可以在主菜单内或子功能内按 ctrl+c,它第一次会按预期工作,但所有后续的 ctrl+c 信号都将被忽略。
我觉得我的问题与这些非常接近:
尽管在他们的情况下,他们在新进程中调用子进程,而我正在调用源函数,我认为这对 fg/bg 不会有相同的影响,对吗?
举个最简单的例子,假设我有一个名为 main.sh:
的文件
trap catchInt SIGINT
reallyQuit=0
function catchInt(){
echo "caught sigint"
if (( $reallyQuit > 0 ));then
echo "really quitting."
exit 1
else
let reallyQuit++
echo "reallyquit is now $reallyQuit"
fi
menu
}
function menu(){
read -ep $'Please choose a number and press enter.\n\t' input
case $input in
1)
child
menu
;;
*)
echo "Quitting"
exit 0
;;
esac
}
source child.sh
# I also source other scripts with utility functions and exported vars
menu
如果我在同一目录中有一个名为 child.sh 的文件:
function child(){
read -p "Please type something"
# I also use utility functions and exported vars
}
下面是上述代码的示例 运行,我在菜单中按了 ctrl+c,然后在子函数中再次尝试:
bash main.sh
Please choose a number and press enter.
caught sigint
reallyquit is now 1
Please choose a number and press enter.
1
Please type something^C^C^C^C^C (I finally pressed enter)
Please choose a number and press enter.
(I tapped ctrl-c a few times here and finally pressed enter)
Quitting
这是一个例子,我先输入 1,然后按 ctrl-c:
bash main.sh
Please choose a number and press enter.
1
Please type something^Ccaught sigint
reallyquit is now 1
Please choose a number and press enter.
(I tapped ctrl-c a few times here and finally pressed enter)
Quitting
如何让陷阱在每次发送 INT 信号时响应?
我不确定,但我认为这是因为第二次调用 menu
时您仍在 "trap handler" 中。因为仍在处理一个 sigint,所以不会处理第二个 sigint。如果您删除该调用并将 menu
换成 while true; do ...; done
,它确实有效:
#! /bin/bash
reallyQuit=0
function catchInt(){
echo "caught sigint"
if (( $reallyQuit > 0 ));then
echo "really quitting."
exit 1
else
let reallyQuit++
echo "reallyquit is now $reallyQuit"
fi
}
trap catchInt SIGINT
function menu(){
read -ep $'Please choose a number and press enter.\n\t' input
case $input in
1)
child
menu
;;
*)
echo "Quitting"
exit 0
;;
esac
}
function child(){
read -p "Please type something"
# I also use utility functions and exported vars
}
# I also source other scripts with utility functions and exported vars
while true; do
menu
done
exit 0
编辑:
为什么要在 main.sh 中包含 child.sh?通常人们会创建通用函数并将其包含在子脚本中。这样您就可以在 child1.sh,..., childN.sh 之间共享 main.sh 中的函数。如果将 source main.sh
添加到 child.sh,陷阱也会起作用。
我需要帮助来理解 SIGINT 是如何在 shell 脚本的主要流程和该脚本中的函数调用之间传播的。对于我的脚本,我有一个主菜单,它接受用户输入并根据该输入调用子函数。子函数与用户交互。
我的目标如下:
- 我需要子函数能够访问主脚本中的函数定义和变量,因此子shell不是一个好的选择
- 子函数应该在它自己的文件中,否则我的主要 shell 脚本会太大而且不实用
- 我想在子函数中使用 ctrl+c return 用户进入主菜单
- 我想在主菜单中按 ctrl+c 理想地打印一些东西,如果再次完成,则退出。如果它必须第一次退出是可以接受的
我看到的行为是,我可以在主菜单内或子功能内按 ctrl+c,它第一次会按预期工作,但所有后续的 ctrl+c 信号都将被忽略。
我觉得我的问题与这些非常接近:
尽管在他们的情况下,他们在新进程中调用子进程,而我正在调用源函数,我认为这对 fg/bg 不会有相同的影响,对吗?
举个最简单的例子,假设我有一个名为 main.sh:
的文件trap catchInt SIGINT
reallyQuit=0
function catchInt(){
echo "caught sigint"
if (( $reallyQuit > 0 ));then
echo "really quitting."
exit 1
else
let reallyQuit++
echo "reallyquit is now $reallyQuit"
fi
menu
}
function menu(){
read -ep $'Please choose a number and press enter.\n\t' input
case $input in
1)
child
menu
;;
*)
echo "Quitting"
exit 0
;;
esac
}
source child.sh
# I also source other scripts with utility functions and exported vars
menu
如果我在同一目录中有一个名为 child.sh 的文件:
function child(){
read -p "Please type something"
# I also use utility functions and exported vars
}
下面是上述代码的示例 运行,我在菜单中按了 ctrl+c,然后在子函数中再次尝试:
bash main.sh
Please choose a number and press enter.
caught sigint
reallyquit is now 1
Please choose a number and press enter.
1
Please type something^C^C^C^C^C (I finally pressed enter)
Please choose a number and press enter.
(I tapped ctrl-c a few times here and finally pressed enter)
Quitting
这是一个例子,我先输入 1,然后按 ctrl-c:
bash main.sh
Please choose a number and press enter.
1
Please type something^Ccaught sigint
reallyquit is now 1
Please choose a number and press enter.
(I tapped ctrl-c a few times here and finally pressed enter)
Quitting
如何让陷阱在每次发送 INT 信号时响应?
我不确定,但我认为这是因为第二次调用 menu
时您仍在 "trap handler" 中。因为仍在处理一个 sigint,所以不会处理第二个 sigint。如果您删除该调用并将 menu
换成 while true; do ...; done
,它确实有效:
#! /bin/bash
reallyQuit=0
function catchInt(){
echo "caught sigint"
if (( $reallyQuit > 0 ));then
echo "really quitting."
exit 1
else
let reallyQuit++
echo "reallyquit is now $reallyQuit"
fi
}
trap catchInt SIGINT
function menu(){
read -ep $'Please choose a number and press enter.\n\t' input
case $input in
1)
child
menu
;;
*)
echo "Quitting"
exit 0
;;
esac
}
function child(){
read -p "Please type something"
# I also use utility functions and exported vars
}
# I also source other scripts with utility functions and exported vars
while true; do
menu
done
exit 0
编辑:
为什么要在 main.sh 中包含 child.sh?通常人们会创建通用函数并将其包含在子脚本中。这样您就可以在 child1.sh,..., childN.sh 之间共享 main.sh 中的函数。如果将 source main.sh
添加到 child.sh,陷阱也会起作用。