代码之家  ›  专栏  ›  技术社区  ›  Ryan C. Thompson

如何从shell脚本中判断调用它的shell是否是交互式shell?

  •  1
  • Ryan C. Thompson  · 技术社区  · 15 年前

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

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

    但是,这失败了,因为脚本是由一个新的非交互shell运行的,该shell是 #!/bin/sh 在顶部。如果我源代码而不是运行脚本,它会按需要工作,但这是一个丑陋的黑客。我宁愿让它在我运行的时候工作。

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

    6 回复  |  直到 12 年前
        1
  •  1
  •   Dennis Williamson    15 年前

    尝试一下,看看它是否满足您的需求:

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

    下划线( $_ )扩展到用于调用脚本的绝对路径名。零点 $0 )扩展到脚本的名称。如果它们不同,则调用脚本 交互式外壳。在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.
    
        2
  •  1
  •   schot    15 年前

    $_ 可能不适用于每个与posix兼容的sh,尽管它可能适用于must。

    $PS1 仅当外壳是交互式的时才设置。所以这应该是有效的:

    if [ -z "$PS1" ]; then
        echo noninteractive
    else
        echo interactive
    fi
    
        3
  •  0
  •   Charlie Oliver    15 年前

    尝试 tty

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

    男士TTY: TTY实用程序写入附加到标准的终端的名称 输入到标准输出。写入的名称是字符串 由ttyname(3)返回。如果标准输入不是终端, 已写入消息“not a tty”。

        4
  •  0
  •   user390948    15 年前

    你可以尝试使用像…

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

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

        5
  •  0
  •   Paul Whittaker    12 年前

    简单回答: 不要在``或 [ ] .

    这里不需要这些构造。


    很明显,我不能确定你期望的是什么

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

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

    该代码将执行以下操作:

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

    一些可能的问题:

    • 这个 -qs 选项到 grep 应该会导致它不产生输出,所以我希望 [ 不管什么都要测试空字符串 $- 看起来像。
    • 反斜杠也有可能从美元符号中溢出,并导致将文字“美元减”(而不是变量的内容)发送到 格雷普 .

    另一方面,如果你移除了 [ 然后说

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

    然后:

    • 你现在的外壳会膨胀 "$-" 使用要测试的值,
    • echo ... | 会把那个寄给 格雷普 在其标准输入上,
    • 格雷普 当输入包含字母时,将返回成功的返回代码 i ,
    • 格雷普 将不打印输出,因为 -QS 旗帜,以及
    • 这个 if 语句将使用 格雷普 的返回代码来决定要采用哪个分支。

    也:

    • 任何倒计时都不会用运行时生成的输出替换任何命令,并且
    • [ 命令将尝试替换的返回代码 格雷普 其中包含一些返回代码,它试图从 格雷普 .

    有关如何使用 如果 命令,见 this section 最好的巴什维德。

        6
  •  0
  •   Paul Whittaker    12 年前

    如果要测试 $- 无需外部流程(例如 grep )然后可以使用以下技术:

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

    这将删除的所有匹配项 i* 从…的价值 $ 然后检查一下是否有什么不同。


    (The ${parameter/from/to} 构建(例如) [ "${-//[!i]/}" = "i" ] 是真的iff interactive)可以在bash脚本中使用,但不存在于dash中,即 /bin/sh 在Debian和Ubuntu系统上。)