How to detect if my shell script is running through a pipe?
我如何从shell脚本中检测它的标准输出是否被发送到终端,或者它是否被管道传输到另一个进程?
举例来说:我想在彩色输出中添加转义代码,但只在交互运行时添加,而不是在管道中添加,类似于
在纯POSIX外壳中,
1 | if [ -t 1 ] ; then echo terminal; else echo"not a terminal"; fi |
返回"terminal",因为输出被发送到您的终端,而
1 | (if [ -t 1 ] ; then echo terminal; else echo"not a terminal"; fi) | cat |
返回"not a terminal",因为parenthetic的输出通过管道传输到
-t fd True if file descriptor fd is open and refers to a terminal.
…其中
1 2 3 | 0: stdin 1: stdout 2: stderr |
没有简单的方法来确定stdin、stdout或stderr是否正在通过管道与脚本进行连接,这主要是因为像
例如,以下bash解决方案在交互式shell中正常工作:
1 2 3 4 5 6 7 8 | [[ -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' |
但它们并不总是有效的
但是,当以非tty
1 2 3 4 5 6 7 8 9 10 11 | # 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 ${?}' |
为什么它重要
这是一个相当大的问题,因为它意味着bash脚本无法判断非tty
当您想编写一个行为类似于已编译实用程序(如
1 2 | ssh -t localhost 'echo piped | cat - <( echo substituted )' ssh -T localhost 'echo piped | cat - <( echo substituted )' |
只有当您能够可靠地确定是否涉及管道时,才能执行类似的操作。否则,在管道或重定向没有可用输入时执行读取stdin的命令将导致脚本挂起并等待stdin输入。
其他不起作用的东西在试图解决这个问题的过程中,我研究了一些未能解决问题的技术,其中包括:
- 检查ssh环境变量
- 在/dev/stdin文件描述符上使用
stat 。 - 通过
[["${-}" =~ 'i' ]] 检查交互模式 - 通过
tty 和tty -s 检查tty状态 - 通过
[["$(ps -o comm= -p $PPID)" =~ 'sshd' ]] 检查ssh 状态
请注意,如果您使用的操作系统支持
我对解决这个问题非常感兴趣,所以请告诉我,如果您想到任何其他可能有效的技术,最好是在Linux和BSD上都有效的基于POSIX的解决方案。
命令
1 2 3 | if [ -t 1 ]; then # stdout is a tty fi |
见"
您没有提到正在使用的shell,但是在bash中,您可以这样做:
1 2 3 4 5 6 7 | #!/bin/bash if [[ -t 1 ]]; then # stdout is a terminal else # stdout is not a terminal fi |
在Solaris上,Dejay Clayton的建议主要起作用。-p没有按需响应。
bash_redir_test.sh如下所示:
1 2 3 4 5 6 7 8 | [[ -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' |
在Linux上,它工作得很好:
1 2 3 4 5 6 7 8 9 10 11 | :$ ./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 |
关于Solaris:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | :# ./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 :# |
以下代码(仅在Linux bash 4.4中测试)不应视为可移植的,也不建议使用,但为了完整起见,这里是:
1 | ls /proc/$$/fdinfo/* >/dev/null 2>&1 || grep -q 'flags: 00$' /proc/$$/fdinfo/0 && echo"pipe detected" |
我不知道为什么,但似乎文件描述符"3"是在bash函数有stdin管道时创建的。
希望它有帮助,