【问题标题】:How can I tell from a within a shell script if the shell that invoked it is an interactive shell?如果调用它的 shell 是交互式 shell,我如何从 shell 脚本中判断?
【发布时间】:2010-07-12 23:06:32
【问题描述】:

我正在尝试设置一个 shell 脚本,该脚本仅在从交互式 shell 调用时才会启动 screen 会话(或重新加入现有会话)。我看到的解决方案是检查$-是否包含字母“i”:

#!/bin/sh -e

echo "Testing interactivity..."
echo 'Current value of $- = '"$-"
if [ `echo \$- | grep -qs i` ]; then
  echo interactive;
else
  echo noninteractive;
fi

但是,这会失败,因为脚本是由一个新的非交互式 shell 运行的,它是由顶部的 #!/bin/sh 调用的。如果我获取脚本而不是运行它,它会按需要工作,但这是一个丑陋的 hack。我宁愿在我运行它时让它工作。

那么如何测试脚本中的交互性?

【问题讨论】:

  • 我只想指出你的shebang说“sh”,但你的问题说“bash”。如果您希望脚本中包含 Bash 功能,则应将 shebang 设置为 #!/bin/bash(即使在您的系统上,sh 符号链接到 bash)。

标签: environment-variables interactive sh subshell


【解决方案1】:

试一试,看看它是否符合您的要求:

#!/bin/sh
if [ $_ != $0 ]
then
  echo interactive;
else
  echo noninteractive;
fi

下划线 ($_) 扩展为用于调用脚本的绝对路径名。零 ($0) 扩展为脚本的名称。如果它们不同,则脚本是从交互式 shell 调用的。在 Bash 中,$_ 的后续扩展将扩展参数提供给前一个命令(最好将$_ 的值保存在另一个变量中以保留它)。

来自man bash

   0      Expands to the name of the shell or shell script.  This  is  set
          at shell initialization.  If bash is invoked with a file of com‐
          mands, $0 is set to the name of that file.  If bash  is  started
          with  the  -c option, then $0 is set to the first argument after
          the string to be executed, if one is present.  Otherwise, it  is
          set  to  the file name used to invoke bash, as given by argument
          zero.
   _      At shell startup, set to the absolute pathname  used  to  invoke
          the  shell or shell script being executed as passed in the envi‐
          ronment or argument list.  Subsequently,  expands  to  the  last
          argument  to the previous command, after expansion.  Also set to
          the full pathname used  to  invoke  each  command  executed  and
          placed in the environment exported to that command.  When check‐
          ing mail, this parameter holds the name of the  mail  file  cur‐
          rently being checked.

【讨论】:

  • 这似乎在 bash、dash 和 zsh 中正常工作。愿意解释一下魔法吗?
  • @Ryan:我认为它也适用于 ksh。我会用一些文档来编辑我的答案。
  • 请注意,只有在脚本开头使用 $_ 时,它才会以这种方式工作。
【解决方案2】:

$_ 可能无法在所有 POSIX 兼容的 sh 中使用,尽管它可能在必须中使用。

$PS1 只有在 shell 是交互式的时候才会被设置。所以这应该有效:

if [ -z "$PS1" ]; then
    echo noninteractive
else
    echo interactive
fi

【讨论】:

  • 不应该是-n "$PS1"吗?
  • @Ryan 很好,你是对的。我换了 then/else 的身体。
  • 唉,对于所有 shell,不是真的dash(至少在 Debian 和 Ubuntu 系统上是 /bin/sh)设置 PS1 是否是交互式的。 printf '#!/bin/sh\necho "$PS1"\n' > check.sh ; chmod +x check.sh ; ./check.sh 在这个 Ubuntu 12.04 系统上输出 $
【解决方案3】:

试试tty

if tty 2>&1 |grep not ; then echo "Not a tty"; else echo "a tty"; fi

man tty: tty 实用程序将终端的名称写入标准 输入到标准输出。写入的名称是字符串 由 ttyname(3) 返回。如果标准输入不是终端,则 消息 ``not a tty'' 被写入。

【讨论】:

    【解决方案4】:

    您可以尝试使用类似...

    if [[ -t 0 ]]
    then
         echo "Interactive...say something!"
         read line
    echo $line
    else
         echo "Not Interactive"
    fi
    

    测试字段中的“-t”开关检查给定的文件描述符是否与终端匹配(例如,如果输出要打印到终端,您也可以这样做来停止程序)。在这里它检查程序的标准输入是否与终端匹配。

    【讨论】:

      【解决方案5】:

      简单回答:不要在 ` ` 或 [ ] 中运行这些命令。

      这里不需要这两种结构。


      显然我无法确定你的预期

      [ `echo \$- | grep -qs i` ]
      

      正在测试,但我认为它不是在测试你认为它在测试的东西。

      该代码将执行以下操作:

      • 在子shell 中运行echo \$- | grep -qs i(由于` `)。
      • 捕获子外壳的标准输出。
      • 用包含该输出的字符串替换原来的 ` ` 表达式。
      • 将该字符串作为参数传递给[ 命令或内置命令(取决于您的shell)。
      • 仅当该字符串不为空时,才能从[ 生成成功的返回码(假设该字符串看起来不像[ 的选项)。

      一些可能的问题:

      • grep-qs 选项应该会导致它不产生任何输出,所以我希望 [ 会测试一个空字符串,而不管 $- 是什么样的。
      • 也有可能是反斜杠转义了美元符号并导致将文字“美元减号”(而不是变量的内容)发送到grep

      另一方面,如果您删除了 [ 和反引号,而是说

      if echo "$-" | grep -qs i ; then
      

      然后:

      • 您当前的 shell 将使用您要测试的值扩展 "$-"
      • echo ... | 会将其发送到 grep 的标准输入,
      • 当输入包含字母 i 时,grep 将返回成功的返回码,
      • 由于-qs 标志,grep 不会打印任何输出,并且
      • if 语句将使用grep 的返回码来决定采用哪个分支。

      还有:

      • 没有反引号将任何命令替换为运行时产生的输出,并且
      • 没有[ 命令会尝试将grep 的返回码替换为它试图从grep 生成的输出中自行重构的一些返回码。

      有关如何使用 if 命令的更多信息,请参阅优秀 BashGuide 的 this section

      【讨论】:

        【解决方案6】:

        如果您想在不派生外部进程的情况下测试$- 的值(例如grep),那么您可以使用以下技术:

        if [ "${-%i*}" != "$-" ]
        then
            echo Interactive shell
        else
            echo Not an interactive shell
        fi
        

        这会从 $- 的值中删除与 i* 的任何匹配项,然后检查这是否有任何不同。


        ${parameter/from/to} 构造(例如,[ "${-//[!i]/}" = "i" ] 是 true iff 交互式)可以在 Bash 脚本中使用,但在 Dash 中不存在,在 Debian 和 Ubuntu 系统上为 /bin/sh。)

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2013-06-19
          • 1970-01-01
          • 2012-01-11
          • 1970-01-01
          • 1970-01-01
          • 2011-03-12
          • 2015-06-29
          相关资源
          最近更新 更多