[英]How to detect when a bash script is triggered from keybinding
我有一个需要用户输入的 Bash 脚本。 可以通过在终端中调用它或按下在 i3(或 Sway)配置文件中注册的键盘快捷键来运行它,如下所示:
bindsym --release $mod+Shift+t exec /usr/local/bin/myscript
我知道我可以使用read -p
在终端中进行提示,但是当通过键绑定触发脚本时,这显然不起作用。 在这种情况下,我可以使用Yad 之类的东西来创建 GUI,但是我无法检测到它何时不在“终端中”。 基本上我希望能够做这样的事情:
if [ $isInTerminal ]; then
read -rp "Enter your username: " username
else
username=$(yad --entry --text "Enter your username:")
fi
我如何(自动)检查我的脚本是从键绑定调用的,还是在终端中运行的? 理想情况下,这应该没有用户指定的命令行参数。 其他人可能会使用该脚本,因此,我想避免通过“忘记标志”引入任何用户错误的可能性。
这个问题建议检查stdout
是否进入终端,所以我创建了这个测试脚本:
#!/usr/bin/env bash
rm -f /tmp/detect.log
logpass() {
echo "$1 IS opened on a terminal" >> /tmp/detect.log
}
logfail() {
echo "$1 IS NOT opened on a terminal" >> /tmp/detect.log
}
if [ -t 0 ]; then logpass stdin; else logfail stdin; fi
if [ -t 1 ]; then logpass stdout; else logfail stdout; fi
if [ -t 2 ]; then logpass stderr; else logfail stderr; fi
但是,这并不能解决我的问题。 无论我是在终端中运行./detect.sh
还是通过 keybind 触发它,输出总是相同的:
$ cat /tmp/detect.log
stdin IS opened on a terminal
stdout IS opened on a terminal
stderr IS opened on a terminal
解决实际问题的最简单方法似乎是将 i3 绑定更改为
bindsym --release $mod+Shift+t exec /usr/local/bin/myscript fromI3
并做
if [[ -n "$1" ]]; then
echo "this was from a keybind"
else
echo "this wasn't from a keybind"
fi
在你的脚本中。
正如 Google 上的大多数结果所建议的那样,您可以使用tty
,当它不在终端中运行时,它通常会返回“not a tty” 。 但是,这似乎与在 i3/Sway 中通过bindsym exec
调用的脚本不同:
/dev/tty1 # From a keybind
/dev/pts/6 # In a terminal
/dev/tty2 # In a console
虽然tty | grep pts
tty | grep pts
会在一定程度上回答这个问题,它无法区分在控制台中运行与在尝试显示 GUI 时不需要的键绑定。
通过键绑定触发似乎总是将systemd
作为父进程。 考虑到这一点,这样的事情可以工作:
{
[ "$PPID" = "1" ] && echo "keybind" || echo "terminal"
} > /tmp/detect.log
systemd
进程总是将1
作为其 PID 可能是一个安全的假设,但不能保证每个使用 i3 的系统也将使用 systemd,因此最好避免这种情况。
更强大的方法是使用ps
。 根据手册页中的PROCESS STATE CODES
:
对于 BSD 格式,当使用 stat 关键字时,可能会显示额外的字符:
< high-priority (not nice to other users) N low-priority (nice to other users) L has pages locked into memory (for real-time and custom IO) s is a session leader l is multi-threaded (using CLONE_THREAD, like NPTL pthreads do) + is in the foreground process group
这里的关键是最后一行的+
。 要在终端中检查,您可以调用ps -Sp <PID>
,它在“交互式”运行时将具有Ss
的STAT
值,如果它是通过键绑定触发的,则调用S+
。
由于您只需要STATE
列,您可以使用-o stat=
进一步清理它,这也将删除标题,然后通过 grep 管道为您提供以下内容:
is_interactive() {
ps -o stat= -p $$ | grep -q '+'
}
if is_interactive; then
read -rp "Enter your username: " username
else
username=$(yad --entry --text "Enter your username:")
fi
这不仅适用于终端模拟器和通过 i3/Sway 键绑定,甚至适用于原始控制台窗口,使其成为比上面的tty
更可靠的选项。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.