[英]How to detect if my shell script is running through a pipe?
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
功能。
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: ...其中
fd
可以是通常的文件描述符分配之一:
0: stdin
1: stdout
2: stderr
There is no foolproof way to determine if STDIN, STDOUT, or STDERR are being piped to/from your script, primarily because of programs like ssh
. 没有万无一失的方法可以确定STDIN,STDOUT或STDERR是否通过管道进出脚本,主要是因为像
ssh
这样的程序。
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'
However, when executing this command as a non-TTY ssh
command, STD streams always looks like they are being piped. 但是,当执行此命令作为非TTY
ssh
命令时,STD流看起来总是看起来像是被管道传输。 To demonstrate this, using STDIN because it's easier: 为了证明这一点,使用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 ${?}'
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. 这是一个非常重要的事情,因为它意味着bash脚本无法判断是否正在管道非tty
ssh
命令。 Note that this unfortunate behavior was introduced when recent versions of ssh
started using pipes for non-TTY STDIO. 请注意,当最新版本的
ssh
开始使用非TTY STDIO管道时,会引入这种不幸的行为。 Prior versions used sockets, which COULD be differentiated from within bash by using [[ -S ]]
. 以前的版本使用套接字,可以通过使用
[[ -S ]]
来区分bash。
This limitation normally causes problems when you want to write a bash script that has behavior similar to a compiled utility, such as cat
. 当您要编写具有与已编译实用程序类似的行为的bash脚本(例如
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: 例如,
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输入。
In trying to solve this problem, I've looked at several techniques that fail to solve the problem, including ones that involve: 在尝试解决这个问题时,我已经研究了几种无法解决问题的技术,包括涉及的问题:
stat
on /dev/stdin file descriptors stat
[[ "${-}" =~ 'i' ]]
[[ "${-}" =~ 'i' ]]
检查交互模式 tty
and tty -s
tty
和tty -s
检查tty状态 ssh
status via [[ "$(ps -o comm= -p $PPID)" =~ 'sshd' ]]
[[ "$(ps -o comm= -p $PPID)" =~ 'sshd' ]]
检查ssh
状态 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. 请注意,如果您使用的是支持
/proc
虚拟文件系统的操作系统,则可以按照STDIO的符号链接来确定是否正在使用管道。 However, /proc
is not a cross-platform, POSIX-compatible solution. 但是,
/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. 我非常有兴趣解决这个问题,所以如果你想到任何其他可能有效的技术,请告诉我,最好是基于POSIX的解决方案,适用于Linux和BSD。
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
”
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
On Solaris, the suggestion from Dejay Clayton works mostly. 在Solaris上,Dejay Clayton的建议主要起作用。 The -p does not respond as desired.
-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
:#
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. 我不知道为什么,但似乎文件描述符“3”以某种方式在bash函数具有STDIN管道时创建。
Hope it helps, 希望能帮助到你,
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.