我有一个 Bash 脚本,我试图制作它来帮助我运行一个相当复杂的命令,并进行一些小的更改,它会通过 echo 和 read 询问我有关的信息。
我已经找到了强制它运行终端来执行命令的解决方案,但我对此不感兴趣。我希望它做的是,如果我在 Nautilus 中留出空间并按 Enter 键(使其与运行软件一起运行),它只会轻轻地弹出一条通知,显示“请从终端运行此程序”。
我可以让弹出窗口发生——就像我知道命令一样——但我无法让 Bash 脚本来判断它是否在终端内运行,它似乎总是这么认为。有可能吗?
答案1
从man bash
下面条件表达式:
-t fd
True if file descriptor fd is open and refers to a terminal.
假设 fd 1 是标准输出,if [ -t 1 ]; then
应该适合您。这高级 Shell 脚本编写指南使用这种方式的声明-t
将进行故障转移ssh
,因此测试(使用标准输入,而不是标准输出)应该是:
if [[ -t 0 || -p /dev/stdin ]]
-p
测试文件是否存在并且是命名管道。 然而,我注意到,根据经验,这对我来说并不正确:-p /dev/stdin
普通终端和 ssh 会话都失败,而if [ -t 0 ]
(或-t 1
) 在这两种情况下都有效(另请参阅下面关于该部分中问题的 Gilles 评论高级 Shell 脚本编写指南)。
如果主要问题是一个专门的上下文,您希望从中调用脚本以适合该上下文的方式运行,那么您可以回避所有这些技术细节,并通过使用包装器和自定义变量来省去一些麻烦:
!#/bin/bash
export SPECIAL_CONTEXT=1
/path/to/real/script.sh
调用这个live_script.sh
或其他名称,然后双击它。当然,您可以使用命令行参数完成同样的事情,但是仍然需要一个包装器来使 GUI 文件浏览器中的指向和单击工作正常。
答案2
使用 bash $SLVL 变量来检测 shell 嵌套的级别。在通过双击运行“raw”的脚本中,它将为 1,在终端内运行的脚本中,它将为 2。
#!/bin/bash
if (( SHLVL < 2 )) ; then
echo "Please run this from a terminal."
read -p "Press <Enter> to close this window"
exit 1
fi
# rest of script
注意:测试 (( )) 内的数值变量时不需要 $。
答案3
尽管金发姑娘的答案在典型情况下可能是正确的,但似乎确实存在边缘情况。就我自己而言,我的 xserver 配置为从该 tty 启动tty1
,并且永远不会离开该 tty。如果 Xorgstdout
是 TTY,那么默认情况下客户端似乎会将 TTY 链接到其文件描述符。
这是我解决问题的方法:
#!/bin/bash
isxclient=$( readlink /dev/fd/2 | grep -q 'tty' && [[ -n $DISPLAY ]] ; echo $? )
if [[ ! -t 2 || $isxclient == "0" ]]; then
notify-send "Script wasn't started from an interactive shell"
else
echo "Script was started from an interactive shell"
fi
我还没有测试过它是否适用于更标准的 X 配置,而且我也非常怀疑这是唯一的边缘情况。如果有人找到更普遍适用的解决方案,请回来告诉我们。
答案4
另一种方法是使用 bash 选项设置内部变量$-
.
从.bashrc
,
# If not running interactively, don't do anything
case $- in
*i*) ;;
*) return;;
esac