![使用任意键停止 bash 脚本](https://linux22.com/image/82912/%E4%BD%BF%E7%94%A8%E4%BB%BB%E6%84%8F%E9%94%AE%E5%81%9C%E6%AD%A2%20bash%20%E8%84%9A%E6%9C%AC.png)
我正在编写一个自定义的 gnome-terminal 配置文件,我想用我选择的任何键停止它。按下该键后,我的意图是完成当时的所有命令并启动 su。我尝试在命令中添加 & ,但它不起作用。我附上一些信息:
我的代码:
su --command 'read -n 1 -s key; if [ "$key" == "g" ]; then su; fi;'
echo -n "W"
sleep 0.09
echo -n "e"
sleep 0.09
echo -n "l"
sleep 0.09
echo -n "c"
sleep 0.09
echo -n "o"
sleep 0.09
echo -n "m"
sleep 0.09
echo -n "e"
sleep 0.09
预期结果:
[角色被正确回显,直到......]
[按键按下]
Welcome
root@myuser:~#
答案1
我能得到的类似问题最接近的是这样的......
首先创建一个名为inputproc的脚本:
read -n1
然后创建一个名为 mainscript 的脚本:
array=(W e l c o m e)
buf=0
while pgrep "inputproc" > /dev/null; do
echo -n ${array[buf]}
((buf++))
sleep 0.09
done
#Insert any commands you want to run afterwards here...
然后用这个命令执行它们
./mainscript & ./inputproc
好吧,这正是您想要的。我找不到一种只用一个脚本就能做到这一点的方法,但令人惊讶的是,我找到了一种只用一个终端窗口就能做到这一点的方法。
除了明显的(使用 while 循环来检查是否输入过程在每个 echo 命令之前运行,并使用数组按顺序回显所需的字母)这可以通过运行主脚本运行时在后台运行输入过程在前台。它并非没有缺陷,但我相信这是您可以获得的最接近您想要的结果。
答案2
这在很多层面上都不起作用。主要是,bash 并不适合多线程编程。但如果你打算这样做,你必须意识到两件事:(1) 为了让你的按键读取“线程”工作,它必须拥有对 tty 的独占访问权。 (2)当你把一个进程放在后台时,它自然会从当前进程的tty复制stdin。 (3) 通常 shell 会检测到这一点并停止后台进程。
仅允许前台进程读取或写入终端(如果用户使用 stty tostop 指定)。内核的终端驱动程序会向尝试从终端读取(当 stty tostop 有效时写入)的后台进程发送 SIGTTIN (SIGTTOU) 信号,除非被捕获,否则该进程将挂起。
你可以在这里看到:
otheus@otheus-VirtualBox ~ $ ( while true; read x; do echo $x; done; ) &
[1] 2841
otheus@otheus-VirtualBox ~ $
[1]+ Stopped ( while true; read x; do
echo $x;
done )
所以tty读取进程应该在前台。后台可以完成“工作”,但请确保先关闭标准输入:<&-
要停止“worker”进程,您必须知道它的 pid。为此,您需要在脚本中启用作业控制(默认情况下处于关闭状态)。set -m
做这个。启动它并%-
在按键后杀死它。您可能想确保按键循环在工作人员结束时结束。你做你的按键监控线程:
start_worker &
while jobs %- &>/dev/null; do
read -n 1 -s -t 1 key
# test key
if [[ $key == q ]]; then
kill -1 %-
break
fi
fi
wait
while 条件测试作业是否仍在运行/识别。一旦结束,作业将返回 false,并且循环退出。现在,读取命令还会在 1 秒后超时。最后,循环退出后,等待以确保作业已“清理”。 (如果您正在运行其他作业,它将挂在此处。)
另一件事是:你想要完成的事情本质上是危险的。混合 root 访问和 bash 脚本编写是危险的。这里需要讨论一些主题,例如当用户按下 Control-C 或 Ctl-Z 时会发生什么。
最后,我什至不确定您是否可以将工作线程与su
您的示例中的混合使用。当 su 运行时,它会将当前的 tty 更改为 root,从而阻止您的工作线程向其输出。