如何判断脚本是否从管道执行?
how to tell if a script is executed from a pipe or not?
假设我们有这个简单的脚本(selfie.sh):
echo "[=10=]"
我如何判断它是否以这种方式执行sh selfie.sh
,
例如 cat selfie.sh | sh
?
sh selfie.sh
给这个:
selfie.sh
和cat selfie.sh | sh
输出这个:
sh
我尝试过的:
如果 [ -s "[=19=]" ]
在 pwd
中有一个名为 'sh' 的文件,则无法提供帮助
$BASH_SOURCE
或类似的东西与 POSIX shell 不兼容
这个问题摆在我面前是因为我写了一个名为 shell-utils 的项目,我希望用户在第一次安装时可以这样安装:
curl -L https://raw.githubusercontent.com/oxnz/shell-utils/master/tool/install | sh
但如果他已经拥有该软件的副本并从他的 shell 中调用,如下所示:
sh shell-utils/tool/install.sh
我需要区别对待,采取不同的行动。
试试 -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
您可以使用以下 POSIX 兼容的 shell 函数。
唯一的先决条件是 stdin
表示为文件 /dev/stdin
的 Unix 平台,现在普遍如此。
您只会在一种非常不寻常的情况下得到误报:如果在 采购 脚本时,您 也提供管道输入;例如,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 "[=10=]" ] || [ -x "[=10=]" ] && POSIXLY_CORRECT=1 file -- "[=10=]" | 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'
这是同一函数的注释版本:
#!/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 [=11=] refers to:
# either: a nonexisting file (implies that [=11=] refers to an executable in
# the path)
# or: an executable file that is not text-based (not a shell script)
# Both cases imply that [=11=] 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 "[=11=]" ] || [ -x "[=11=]" ] && POSIXLY_CORRECT=1 file -- "[=11=]" | 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
# )
# 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'
假设我们有这个简单的脚本(selfie.sh):
echo "[=10=]"
我如何判断它是否以这种方式执行sh selfie.sh
,
例如 cat selfie.sh | sh
?
sh selfie.sh
给这个:
selfie.sh
和cat selfie.sh | sh
输出这个:
sh
我尝试过的:
-
如果
[ -s "[=19=]" ]
在pwd
中有一个名为 'sh' 的文件,则无法提供帮助
$BASH_SOURCE
或类似的东西与 POSIX shell 不兼容
这个问题摆在我面前是因为我写了一个名为 shell-utils 的项目,我希望用户在第一次安装时可以这样安装:
curl -L https://raw.githubusercontent.com/oxnz/shell-utils/master/tool/install | sh
但如果他已经拥有该软件的副本并从他的 shell 中调用,如下所示:
sh shell-utils/tool/install.sh
我需要区别对待,采取不同的行动。
试试 -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
您可以使用以下 POSIX 兼容的 shell 函数。
唯一的先决条件是 stdin
表示为文件 /dev/stdin
的 Unix 平台,现在普遍如此。
您只会在一种非常不寻常的情况下得到误报:如果在 采购 脚本时,您 也提供管道输入;例如,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 "[=10=]" ] || [ -x "[=10=]" ] && POSIXLY_CORRECT=1 file -- "[=10=]" | 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'
这是同一函数的注释版本:
#!/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 [=11=] refers to:
# either: a nonexisting file (implies that [=11=] refers to an executable in
# the path)
# or: an executable file that is not text-based (not a shell script)
# Both cases imply that [=11=] 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 "[=11=]" ] || [ -x "[=11=]" ] && POSIXLY_CORRECT=1 file -- "[=11=]" | 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
# )
# 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'