bash - bash SIGINT 陷阱触发一次但不再触发
问题描述
我需要帮助了解 SIGINT 如何在 shell 脚本的主要流程和该脚本内的函数调用之间传播。对于我的脚本,我有一个主菜单,它接受用户输入并根据该输入调用子函数。子函数与用户交互。
我的目标如下:
- 我需要子函数可以访问主脚本中的函数定义和变量,所以子shell不是一个很好的选择
- 子函数应该在它自己的文件中,否则我的主 shell 脚本会太大而且笨重
- 我想在子函数中使用 ctrl+c 将用户返回到主菜单
- 我想在主菜单中使用 ctrl+c 来理想地打印一些东西,如果再次完成,退出。如果它必须第一次退出是可以接受的
我看到的行为是我可以在主菜单内或从子函数内点击 ctrl+c,它会在第一次按预期工作,但所有后续 ctrl+c 信号都被忽略。
我觉得我的问题非常接近这些:
- https://unix.stackexchange.com/questions/240602/how-can-i-handle-sigint-trap-with-a-user-prompt-in-shell-script
- bash:为什么我不能在后台 shell 中为 SIGINT 设置陷阱?
尽管在他们的情况下,他们在新进程中调用子进程,而我正在调用源函数,我认为这对 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
第二次调用时您仍处于“陷阱处理程序”中。因为仍在处理一个信号,所以不会处理第二个信号。如果您要删除该调用并用 包裹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 中,陷阱也将起作用。
推荐阅读
- django - Django 相关对象参考 remove(obj, bulk=False)
- java - 对象数组没有合适的构造函数
- ethereum - web3 ABI 无法在以太坊主网上获得合约所有权
- node.js - 配置数据库时,连接详细信息不可用 Digital Ocean
- hangfire - 如何在 ASP.NET CORE Web API 中配置 Hangfire 以使用 postgresql 作为存储数据库?
- python - 如何从Django中另一个下拉列表的选定值填充下拉列表
- leaflet - 使用自定义控件在传单地图上绘制不同的对象
- tableau-api - 如何将 Tableau Redshift 连接从 dev 环境更改为 qa 环境?
- r - 如何在 R 中转换这些数据?
- c# - C#如何读/写外部控制台的IO