Bash 脚本如何知道它是如何运行的?

Bash 脚本如何知道它是如何运行的?

我有一个 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

相关内容