bash 如何检测我的 shell 脚本是否正在通过管道运行?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/911168/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to detect if my shell script is running through a pipe?
提问by dmckee --- ex-moderator kitten
How do I detect from within a shell script if its standard output is being sent to a terminal or if it's piped to another process?
我如何从 shell 脚本中检测它的标准输出是发送到终端还是通过管道传输到另一个进程?
The case in point: I'd like to add escape codes to colorize output, but only when run interactively, but not when piped, similar to what ls --color
does.
恰当的例子:我想添加转义码来为输出着色,但仅在交互式运行时,而不是在管道时,类似于什么ls --color
。
回答by dmckee --- ex-moderator kitten
In a pure POSIX shell,
在纯 POSIX shell 中,
if [ -t 1 ] ; then echo terminal; else echo "not a terminal"; fi
returns "terminal", because the output is sent to your terminal, whereas
返回“终端”,因为输出被发送到您的终端,而
(if [ -t 1 ] ; then echo terminal; else echo "not a terminal"; fi) | cat
returns "not a terminal", because the output of the parenthetic is piped to cat
.
返回“不是终端”,因为括号的输出通过管道传输到cat
.
The -t
flag is described in man pages as
该-t
标志在手册页中描述为
-t fd True if file descriptor fd is open and refers to a terminal.
-t fd 如果文件描述符 fd 已打开并指向终端,则为真。
... where fd
can be one of the usual file descriptor assignments:
... wherefd
可以是通常的文件描述符分配之一:
0: stdin
1: stdout
2: stderr
回答by Dejay Clayton
There is no foolproofway to determine if STDIN, STDOUT, or STDERR are being piped to/from your script, primarily because of programs like ssh
.
没有万无一失的方法来确定 STDIN、STDOUT 或 STDERR 是否正在通过管道传入/传出您的脚本,主要是因为ssh
.
Things that "normally" work
“正常”工作的东西
For example, the following bash solution works correctly in an interactive shell:
例如,以下 bash 解决方案在交互式 shell 中正常工作:
[[ -t 1 ]] && \
echo 'STDOUT is attached to TTY'
[[ -p /dev/stdout ]] && \
echo 'STDOUT is attached to a pipe'
[[ ! -t 1 && ! -p /dev/stdout ]] && \
echo 'STDOUT is attached to a redirection'
But they don't always work
但它们并不总是有效
However, when executing this command as a non-TTY ssh
command, STD streams alwayslooks like they are being piped. To demonstrate this, using STDIN because it's easier:
但是,当将此命令作为非 TTYssh
命令执行时,STD 流始终看起来像是在通过管道传输。为了证明这一点,使用 STDIN 因为它更容易:
# CORRECT: Forced-tty mode correctly reports '1', which represents
# no pipe.
ssh -t localhost '[[ -p /dev/stdin ]]; echo ${?}'
# CORRECT: Issuing a piped command in forced-tty mode correctly
# reports '0', which represents a pipe.
ssh -t localhost 'echo hi | [[ -p /dev/stdin ]]; echo ${?}'
# INCORRECT: Non-tty mode reports '0', which represents a pipe,
# even though one isn't specified here.
ssh -T localhost '[[ -p /dev/stdin ]]; echo ${?}'
Why it matters
为什么重要
This is a pretty big deal, because it implies that there is no way for a bash script to tell whether a non-tty ssh
command is being piped or not. Note that this unfortunate behavior was introduced when recent versions of ssh
started using pipes for non-TTY STDIO. Prior versions used sockets, which COULD be differentiated from within bash by using [[ -S ]]
.
这是一个非常大的问题,因为它意味着 bash 脚本无法判断是否ssh
正在通过管道传输非 tty命令。请注意,当最近版本ssh
开始使用非 TTY STDIO 的管道时,引入了这种不幸的行为。以前的版本使用套接字,可以通过使用[[ -S ]]
.
When it matters
重要的时候
This limitation normally causes problems when you want to write a bash script that has behavior similar to a compiled utility, such as cat
. For example, cat
allows the following flexible behavior in handling various input sources simultaneously, and is smart enough to determine whether it is receiving piped input regardless of whether non-TTY or forced-TTY ssh
is being used:
当您想要编写行为类似于已编译实用程序的 bash 脚本时,此限制通常会导致问题,例如cat
. 例如,cat
在同时处理各种输入源时允许以下灵活的行为,并且足够智能地确定它是否正在接收管道输入,而不管使用的是非 TTY 还是强制 TTY ssh
:
ssh -t localhost 'echo piped | cat - <( echo substituted )'
ssh -T localhost 'echo piped | cat - <( echo substituted )'
You can only do something like that if you can reliably determine if pipes are involved or not. Otherwise, executing a command that reads STDIN when no input is available from either pipes or redirection will result in the script hanging and waiting for STDIN input.
如果您可以可靠地确定是否涉及管道,则只能执行类似的操作。否则,在管道或重定向中没有可用输入时执行读取 STDIN 的命令将导致脚本挂起并等待 STDIN 输入。
Other things that don't work
其他不起作用的东西
In trying to solve this problem, I've looked at several techniques that fail to solve the problem, including ones that involve:
在尝试解决此问题时,我查看了几种未能解决问题的技术,包括涉及以下内容的技术:
- examining SSH environment variables
- using
stat
on /dev/stdin file descriptors - examining interactive mode via
[[ "${-}" =~ 'i' ]]
- examining tty status via
tty
andtty -s
- examining
ssh
status via[[ "$(ps -o comm= -p $PPID)" =~ 'sshd' ]]
- 检查 SSH 环境变量
- 使用
stat
在/ dev /标准输入文件描述符 - 通过检查交互模式
[[ "${-}" =~ 'i' ]]
- 通过
tty
和检查 tty 状态tty -s
- 检查
ssh
状态通过[[ "$(ps -o comm= -p $PPID)" =~ 'sshd' ]]
Note that if you are using an OS that supports the /proc
virtual filesystem, you might have luck following the symbolic links for STDIO to determine whether a pipe is being used or not. However, /proc
is not a cross-platform, POSIX-compatible solution.
请注意,如果您使用的是支持/proc
虚拟文件系统的操作系统,那么您可能会很幸运地遵循 STDIO 的符号链接来确定是否正在使用管道。但是,/proc
它不是跨平台的、与 POSIX 兼容的解决方案。
I'm extremely interesting in solving this problem, so please let me know if you think of any other technique that might work, preferably POSIX-based solutions that work on both Linux and BSD.
我对解决这个问题非常感兴趣,所以如果您想到任何其他可行的技术,请告诉我,最好是在 Linux 和 BSD 上都可以使用的基于 POSIX 的解决方案。
回答by Beano
The command test
(builtin in bash
), has an option to check if a file descriptor is a tty.
该命令test
(内置于bash
)有一个选项来检查文件描述符是否为 tty。
if [ -t 1 ]; then
# stdout is a tty
fi
See "man test
" or "man bash
" and search for "-t
"
查看“ man test
”或“ man bash
”并搜索“ -t
”
回答by Dan Moulding
You don't mention which shell you are using, but in Bash, you can do this:
你没有提到你使用的是哪个 shell,但在 Bash 中,你可以这样做:
#!/bin/bash
if [[ -t 1 ]]; then
# stdout is a terminal
else
# stdout is not a terminal
fi
回答by sbj3
On Solaris, the suggestion from Dejay Clayton works mostly. The -p does not respond as desired.
在 Solaris 上,Dejay Clayton 的建议最有效。-p 没有按预期响应。
bash_redir_test.sh looks like:
bash_redir_test.sh 看起来像:
[[ -t 1 ]] && \
echo 'STDOUT is attached to TTY'
[[ -p /dev/stdout ]] && \
echo 'STDOUT is attached to a pipe'
[[ ! -t 1 && ! -p /dev/stdout ]] && \
echo 'STDOUT is attached to a redirection'
On Linux, it works great:
在 Linux 上,它工作得很好:
:$ ./bash_redir_test.sh
STDOUT is attached to TTY
:$ ./bash_redir_test.sh | xargs echo
STDOUT is attached to a pipe
:$ rm bash_redir_test.log
:$ ./bash_redir_test.sh >> bash_redir_test.log
:$ tail bash_redir_test.log
STDOUT is attached to a redirection
On Solaris:
在 Solaris 上:
:# ./bash_redir_test.sh
STDOUT is attached to TTY
:# ./bash_redir_test.sh | xargs echo
STDOUT is attached to a redirection
:# rm bash_redir_test.log
bash_redir_test.log: No such file or directory
:# ./bash_redir_test.sh >> bash_redir_test.log
:# tail bash_redir_test.log
STDOUT is attached to a redirection
:#
回答by ATorras
The following code (tested only in linux bash 4.4) should not be considered portable nor recommended, but for the sake of completeness here it is:
以下代码(仅在 linux bash 4.4 中测试)不应被视为可移植或推荐的,但为了完整起见,这里是:
ls /proc/$$/fdinfo/* >/dev/null 2>&1 || grep -q 'flags: 00$' /proc/$$/fdinfo/0 && echo "pipe detected"
I don't know why, but it seems that file descriptor "3" is somehow created when a bash function has STDIN piped.
我不知道为什么,但是当 bash 函数通过 STDIN 管道传输时,似乎以某种方式创建了文件描述符“3”。
Hope it helps,
希望能帮助到你,