检测stdout是否被重定向到管道(而不是文件,字符设备,终端或套接字)?

时间:2014-05-26 15:08:06

标签: linux unix posix sh

理想情况下,这可以在shell中编写脚本,但Perl或Python可以。 C代码可能会有所帮助,但可能会降低成本/收益。

我认识到重定向到FIFO(命名管道)可能与真实管道无法区分, 这就是我不太关心的边缘案例。

严格的POSIX解决方案是最好的,独立于UNIX / Linux变体是最好的,但至少在Darwin(MacOS X)上运行的东西是我现在需要的。

在你写答案之前 - I already know about test -t - 这将告诉我stdout是否是一个终端(在这种情况下它绝对不是一个管道 - 但它不会告诉我stdout是否已被重定向到一个文件,非终端字符设备,或UNIX域套接字而不是管道。

预期用例:我有一个命令应该在shell的反引号中运行, 这样它就可以输出设置环境变量的命令。 如果stdout没有重定向到管道,我希望命令中止错误 因为在这种情况下它绝对没有被eval `mycommand`;调用。

如果在反引号中运行命令时shell会设置一些特殊的环境变量会有所帮助,但由于它可能特定于bash或zsh或其他东西,因此管道检测更为重要

5 个答案:

答案 0 :(得分:3)

您可以对文件描述符执行fstat并检查返回结构,例如,st_mode = 0x1000(S_IFIFO)表示命名管道。

Python示例:

from __future__ import print_function
import sys
import os

print(os.fstat(sys.stdout.fileno()), file=sys.stderr)

Windows上的输出:

C:> python test_fd.py | more
nt.stat_result(st_mode=4096, st_ino=0L, st_dev=0, st_nlink=0, st_uid=0, st_gid=0, st_size=0L, st_atime=0L, st_mtime=0L, st_ctime=0L)

C:> python test_fd.py > test_fd.txt
nt.stat_result(st_mode=33206, st_ino=16888498602769633L, st_dev=0, st_nlink=1, st_uid=0, st_gid=0, st_size=0L, st_atime= 1401119520L, st_mtime=1401119520L, st_ctime=1401119349L)

答案 1 :(得分:1)

stat命令适用于MacOS X和(有一些变体实现)其他Linux / Unix变体,因此它是我的解决方案:

FORMOPT=
TYPEFORM=
stat() {
  if [ -z "$FORMOPT" ]; then
    if /usr/bin/stat --version >/dev/null 2>&1; then
      FORMOPT=--format
      TYPEFORM=%F
    else
      FORMOPT=-f
      TYPEFORM=%HT
    fi
  fi
  case $1 in
    type) FORMARG="$FORMOPT $TYPEFORM" ; shift ;;
  esac
  /usr/bin/stat -L $FORMARG "$@"
}

exec 9>&1
case `stat type /dev/fd/9` in
  [Ff]ifo*) echo stdout is a pipe ;;
  *) echo stdout is not a pipe ;;
esac

答案 2 :(得分:1)

(我的评论根据Alex Dupuy的建议转而回答) 既然你说你可以使用perl,我想你可以使用它的-p文件测试运算符(perldoc.perl.org/functions/-X.html);像perl -e 'exit(-p STDOUT ? 0 : 1);'这样的东西会告诉你stdout是管道还是fifo(没有区分它们)。

Fwiw,从shell调用它正是我使用perl -e的原因:))

答案 3 :(得分:1)

在Python中:

import sys
print sys.stdout.isatty()

示例:

$ ./a
True
$ ./a | less
False

答案 4 :(得分:0)

是一个特定于Linux的解决方案,可以满足我的需求:

exec 9>&1
case `readlink /dev/fd/9` in
    pipe:\[*\]) echo stdout is a pipe ;;
    *) echo stdout is not a pipe ;;
esac

但这不是我要找的答案,因为我希望它也可以在xBSD上运行。

我已经制作了这个答案社区维基,所以随时改进它, 但如果您有非Linux解决方案(或使用其他技术的特定于Linux的解决方案), 请创建一个新答案。