Bash脚本如何知道其运行方式?


10

我正在尝试制作一个Bash脚本,以帮助我运行一个相当复杂的命令,并进行一些小的更改,使其通过echo和read向我询问。

我找到了迫使其运行终端以执行命令的解决方案,但我对此并不感兴趣。我想做的是,如果我在Nautilus中放开并在Enter上按Enter(使其与Run Software一起运行),它会轻轻弹出一个通知,提示“请从终端运行此程序”。

我可以使弹出窗口发生-就像我知道的命令一样-但我无法让Bash脚本来判断它是否在终端中运行,似乎总是这样。可能吗

Answers:


10

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,因此测试(使用stdin而非stdout)应为:

if [[ -t 0 || -p /dev/stdin ]]

-p测试文件是否存在并且是命名管道。 但是,我会从经验上注意到这对我来说是不正确的:-p /dev/stdin对于普通终端和ssh会话均失败,而if [ -t 0 ](或-t 1)在两种情况下均有效(另请参见下面关于高级Shell脚本指南那部分内容的Gilles评论)。


如果主要问题是特定的上下文,您希望从中调用脚本以适合该上下文的方式运行,则可以回避所有这些技术知识,并使用包装器和自定义变量为自己省事:

!#/bin/bash

export SPECIAL_CONTEXT=1
/path/to/real/script.sh

调用此名称live_script.sh或其他名称,然后双击该名称。您当然可以使用命令行参数完成相同的操作,但是仍然需要使用包装器来指向并单击GUI文件浏览器。


5
这是正确的答案-这也是POSIX所说的shell应该如何检测它是否是交互式的。
mikeserv

2
@DanielAmaya-如果您重定向输入,则该脚本不在终端上运行。问题是如何检测脚本是否在终端上运行。
mikeserv

2
你确定使用||[ … ]这样呢?如果使用,[[ … ]]则可以,但是通常||使用来分隔命令,并且由于缺少最后[ -t 0一个,因此是不正确的调用。通常也没有命令。我同意测试终端;那可能就是这样做的方式。这只是我所关心的语法。[]-p
乔纳森·勒夫勒2014年

1
@JonathanLeffler正确;会产生语法错误,因为shell操作符||在所需的最终]参数之前出现[
chepner 2014年

3
《高级Bash脚本指南》中的那一部分有几个错误。PS1判断外壳是否交互式的可靠测试。“如果脚本需要测试它是否在交互式外壳中运行”也令人困惑:应该是某些代码是否需要测试—脚本通常不在交互式外壳中运行(但可以,如果是源代码则可以) 。测试iin $-是测试外壳是否交互式的正确方法。测试-t 0或是-t 2判断脚本是否在终端中运行的正确方法,这不同于交互方式。
吉尔(Gilles)'所以

0

使用bash $ SHLVL变量来检测shell嵌套的级别。在通过双击运行“原始”的脚本中,它将为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

0

尽管Goldilocks的回答在典型情况下可能是正确的,但似乎确实存在一些极端情况。在我自己的情况下,我的xserver配置为从其启动,tty1并且从不离开该tty。如果Xorg stdout是一个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配置上工作,我也非常怀疑这是唯一的优势。如果有人找到更通用的解决方案,请回来告诉我们。


-2

另一个,使用bash选项设置内部变量$-

来自.bashrc

# If not running interactively, don't do anything
case $- in
    *i*) ;;
    *) return;;
esac

交互式外壳不一定必须连接到终端。当以此连接开始的人自动开始互动时,这也是可能的:cmd | sh -i | cmd
mikeserv

此代码正在脚本中执行。即使它在终端中运行,它也不会是交互式的。
吉尔斯(Gilles)'所以
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.