简体   繁体   English

如何判断脚本是否从管道执行?

[英]how to tell if a script is executed from a pipe or not?

suppose we have this simple script(selfie.sh): 假设我们有这个简单的脚本(selfie.sh):

echo "$0"

how do i tell if it is executed this way sh selfie.sh or not, such as cat selfie.sh | sh 我如何判断它是否以这种方式执行sh selfie.sh或者不是,例如cat selfie.sh | sh cat selfie.sh | sh ? cat selfie.sh | sh

sh selfie.sh give this: sh selfie.sh给这个:

selfie.sh

and cat selfie.sh | sh cat selfie.sh | sh cat selfie.sh | sh output this: cat selfie.sh | sh输出这个:

sh

what I have tried: 我试过的:

  • [ -s "$0" ] can't help if there's a file named 'sh' in pwd 如果在pwd有一个名为“sh”的文件,则[ -s "$0" ]无法提供帮助
  • $BASH_SOURCE or something alike is not compatible with POSIX shell $BASH_SOURCE或类似的东西与POSIX shell不兼容

This question comes in front of me cause I have wrote a project named shell-utils , and I want the user could install like this way if this the first time (s)he installs: 这个问题摆在我面前因为我写了一个名为shell-utils的项目,我希望用户可以这样安装,如果这是他第一次安装:

curl -L https://raw.githubusercontent.com/oxnz/shell-utils/master/tool/install | sh

but if (s)he already has a copy of the software and invoke from his shell like this: 但如果(s)他已经拥有该软件的副本并从他的shell调用如下:

sh shell-utils/tool/install.sh

I need to tell the differences and act differently. 我需要说出差异并采取不同的行动。

Try the -t conditional expression. 尝试使用-t条件表达式。

$ cat selfie.sh
[ -t 0 ] && echo "bash is reading a script from somewhere else" || echo "bash is reading a script from stdin"
$ cat selfie.sh | sh
bash is reading a script from stdin
$ sh selfie.sh
bash is reading a script from somewhere else

You can use the following POSIX-compliant shell function. 您可以使用以下符合POSIX标准的shell函数。

The only prerequisite is a Unix platform where stdin is represented as file /dev/stdin , which is generally the case nowadays. 唯一的先决条件是Unix平台,其中stdin表示为文件/dev/stdin ,现在通常就是这种情况。

You'll get a false positive only in one - very unusual - scenario: if, while sourcing a script, you also provide pipeline input; 你只会在一个非常不寻常的场景中得到误报:如果在获取脚本时,你提供管道输入; eg, echo hi | . selfie.sh 例如, echo hi | . selfie.sh echo hi | . selfie.sh

#!/bin/sh

# Indicates via exit code whether the contents of the script at hand
# were provided through a pipe, e.g., `curl .... | sh`.
# Caveat: You'll get a false positive in the following - exotic - corner case:
#         ... | . script # combination of pipeline input and sourcing.
isThisScriptPiped() {
  if [ ! -f "$0" ] || [ -x "$0" ] && POSIXLY_CORRECT=1 file -- "$0" | grep -Fvq 'text'; then
    if POSIXLY_CORRECT=1 file -i /dev/stdin | grep -Fq 'fifo'; then
      return 0
    fi
  fi
  return 1
}

# Sample call
isThisScriptPiped && echo 'PIPED' || echo 'NOT piped'

Here's an annotated version of the same function: 这是相同功能的注释版本

#!/bin/sh

# Note: POSIXLY_CORRECT is set below to make the GNU `file` utility behave
#       in a POSIX-compliant manner so as to report the type of the *target*
#       in the event that the operand is a *symlink*.
#       This *could* happen with a shell executable that is a symlink, and 
#       *definitely* happens with /dev/stdin, which on Linux is a symlink to
#       /proc/self/fd/0.

# Indicates via exit code whether the contents of the script at hand
# were provided through a pipe, e.g., `curl .... | sh`.
# Caveat: You'll get a false positive in the following - exotic - corner case:
#         ... | . script # combination of pipeline input and sourcing.
isThisScriptPiped() {

  # Test 1 of 2: Check if $0 refers to:
  #  either: a nonexisting file (implies that $0 refers to an executable in
  #          the path)
  #  or: an executable file that is not text-based (not a shell script)
  # Both cases imply that $0 refers to a shell executable, which in turn implies
  # that no filename argument (script file path) was passed to the shell.
  # Note that while `file` implementations differ, their output for text-based
  # executables (shell scripts) always contains 'text' (POSIX mandates
  # 'commands text', but neither BSD nor GNU `file` do that).
  if [ ! -f "$0" ] || [ -x "$0" ] && POSIXLY_CORRECT=1 file -- "$0" | grep -Fvq 'text'; then

    # The implication is that the script contents comes from:
    #  - either: stdin - whether through input redirection (sh < script) or
    #            from a pipe (... | sh)
    #  - or: from sourcing (. script)
    # Note that in sh there is no way that I know of that lets you determine
    # reliably whether the script is being sourced. Knowing whether the script
    # is being sourced *or* provided via stdin is as close as you can get.
    # (To check for sourcing in Bash, Ksh, or Zsh, see 
    #  http://stackoverflow.com/a/28776166/45375 )

    # Test 2 of 2:
    #  See if stdin is connected to a pipe, which in combination with test 1
    #  implies that the script contents is being piped, EXCEPT in one scenario:
    #  Caveat: You'll get a false positive in the following - very unusual - 
    #          corner case:
    #            ... | . script # combination of sourcing and pipe input
    #  Note:
    #    - POSIX mandates that when passing a FIFO (named pipe) to `file`
    #      the output contain the string 'fifo', which is true of both BSD
    #      and GNU `file`.
    #    - Option -i is crucial to prevent `file` from trying to
    #      read the *contents* of stdin; with -i, it just reports the basic
    #      file type.
    if POSIXLY_CORRECT=1 file -i /dev/stdin | grep -Fq 'fifo'; then
      return 0
    fi

  fi

  return 1

}

# Sample call
isThisScriptPiped && echo 'PIPED' || echo 'NOT piped'

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM