繁体   English   中英

如何检测何时从键绑定触发 bash 脚本

[英]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> ,它在“交互式”运行时将具有SsSTAT值,如果它是通过键绑定触发的,则调用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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM