我如何从一个shell脚本中检测,如果它的标准输出被发送到终端或如果它被管道到另一个进程?

举个例子:我想添加转义码来着色输出,但只在交互运行时,而不是在管道运行时,类似于ls——color。


当前回答

你没有提到你在使用哪个shell,但是在Bash中,你可以这样做:

#!/bin/bash

if [[ -t 1 ]]; then
    # stdout is a terminal
else
    # stdout is not a terminal
fi

其他回答

在纯POSIX shell中,

if [ -t 1 ] ; then echo terminal; else echo "not a terminal"; fi

返回"terminal",因为输出被发送到您的终端,而

(if [ -t 1 ] ; then echo terminal; else echo "not a terminal"; fi) | cat

返回“非终端”,因为插入元素的输出被管道传输给cat。


-t标志在手册页中描述为

-t fd如果打开文件描述符fd并指向终端,则为True。

... fd可以是常见的文件描述符赋值之一:

0:标准输入 1:标准输出 2:标准误差

命令test(内置在Bash中)有一个选项来检查文件描述符是否是tty。

if [ -t 1 ]; then
    # Standard output is a tty
fi

参见“man test”或“man bash”,搜索“-t”。

你没有提到你在使用哪个shell,但是在Bash中,你可以这样做:

#!/bin/bash

if [[ -t 1 ]]; then
    # stdout is a terminal
else
    # stdout is not a terminal
fi

下面的代码(仅在Linux Bash 4.4中测试)不应该被认为是可移植的,也不推荐,但为了完整性,这里是:

ls /proc/$$/fdinfo/* >/dev/null 2>&1 || grep -q 'flags:    00$' /proc/$$/fdinfo/0 && echo "pipe detected"

我不知道为什么,但当Bash函数具有标准输入管道时,似乎以某种方式创建了文件描述符“3”。

在Solaris上,来自Dejay Clayton的建议最有效。-p没有按期望的方式响应。

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'

在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

在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

:#