如何从shell中检查stdin是否为/ dev / null?


9

在Linux上,shell脚本是否可以检查其标准输入是否从空设备(1,3)*重定向,理想情况下不读取任何内容?

预期的行为将是:

./checkstdinnull
-> no
./checkstdinnull < /dev/null
-> yes
echo -n | ./checkstdinnull
-> no

EDIT

mknod secretunknownname c 1 3
exec 6<secretunknownname
rm secretunknownname
./checkstdinnull <&6
-> yes

我怀疑我“只是”需要读取输入设备的maj / min号。但是我找不到从外壳执行此操作的方法。


*没有必要/dev/null,但是任何空设备,即使使用手动创建也是如此mknod


2
您是否需要知道它/dev/null是tty还是不是tty?
roaima '18

{ readlink -f /dev/stdin; } <&6对于使用exec并删除了该节点的情况,输出为/root/secretunknownname (deleted)。正如它表明该文件已被删除:满足您的需求还不够吗?
以撒

需要(实际上是“需要”)知道标准输入是否为空设备。
西尔文·勒鲁

2
我试图在(非常糟糕的设计!)工业系统中找到自己的出路。有时,它会将worker实用程序的输入映射到空设备,或者有时将其映射到实际的硬件设备。在两种情况下,都使用相同的代码,但是具有不同的主要/次要开发编号。我们正在尝试跟踪何时(以及为什么!)有时选择使用一个或另一个。目前,该stat解决方案是唯一可行的解决方案。
西尔文·勒鲁

“因此,您的EDIT上的示例并没有真正重现您描述的问题,或者:是吗?” 是的 输入从空设备重定向。通常可以通过进行访问/dev/null,但不是必需的。您可以mknod在我的示例中使用来“别名” 。
西尔文·勒鲁

Answers:


18

在linux上,您可以执行以下操作:

stdin_is_dev_null(){ test "`stat -Lc %t:%T /dev/stdin`" = "`stat -Lc %t:%T /dev/null`"; }

在没有stat(1)的Linux上(例如,路由器上的busybox):

stdin_is_dev_null(){ ls -Ll /proc/self/fd/0 | grep -q ' 1,  *3 '; }

在* bsd上:

stdin_is_dev_null(){ test "`stat -f %Z`" = "`stat -Lf %Z /dev/null`"; }

在诸如* bsd和solaris之类的系统上/dev/stdin/dev/fd/0并且/proc/PID/fd/0不是Linux上的“魔术”符号链接,而是在打开时将切换到实际文件的字符设备。它们路径上的stat(2)返回的结果与打开的文件描述符上的fstat(2)不同。

这意味着即使安装了GNU coreutils,Linux示例也无法在此处运行。如果GNU stat(1)的版本足够新,则可以使用该-参数让它在文件描述符0上执行fstat(2),就像* bsd中的stat(1)一样:

stdin_is_dev_null(){ test "`stat -Lc %t:%T -`" = "`stat -Lc %t:%T /dev/null`"; }

用提供fstat(2)接口的任何语言进行移植检查也很容易。在perl

stdin_is_dev_null(){ perl -e 'exit((stat STDIN)[6]!=(stat "/dev/null")[6])'; }

1
由于设备类型/dev/null1:3,您可以立即对其进行测试。
RudiC '18

12
FWIW问题不在于外壳语法。对于与我的问题有关的问题,如果答案仅使我指向stat命令,那我将已经非常满意。我看不到`$(支持者之间展开编辑大战的意义。就我个人而言,我更喜欢$(...)POSIX的一些基本原理(pubs.opengroup.org/onlinepubs/9699919799/xrat/…),但是我不认为在与本主题无关的其他问题上做出正确的回答题。
西尔文·勒鲁

2
有人可以$( ... )在这个答案的背景下解释为什么优先于反引号吗?
user1717828

那可以解决什么错误 ”?它不能修复错误。该$(..)风格巢更容易,更AIUI是POSIX首选方法。我当然不打算发动任何形式的编辑之战,而是倾向于提出建议而不是改变您的出色答案。
roaima '18

@ user1717828看到这里这里这里的一些清晰度。
roaima '18

16

在Linux上,要确定是否从重定向标准输入/dev/null,可以检查是否/proc/self/fd/0具有与以下相同的设备和inode /dev/null

if [ /proc/self/fd/0 -ef /dev/null ]; then echo yes; else echo no; fi

您可以使用/dev/stdin代替/proc/self/fd/0

如果要检查是否从空设备重定向了标准输入,则需要比较主要和次要设备编号,例如使用stat(另请参见mosvy的答案):

if [ "$(stat -Lc %t:%T /dev/stdin)" = "$(stat -Lc %t:%T /dev/null)" ]; then echo yes; else echo no; fi

或者,如果您不关心这是特定于Linux的

if [ "$(stat -Lc %t:%T /dev/stdin)" = "1:3" ]; then echo yes; else echo no; fi

2
-ef,如果FILE1和FILE2引用相同的设备和inode,则为True
Rui F Ribeiro

很酷。bash -c 'ls -l /proc/self/fd/0 /dev/null; [[ /proc/self/fd/0 -ef /dev/null ]] && echo dev null'然后再用</dev/null。+1
格伦·杰克曼(Glenn jackman)

1
/dev/stdin是一个符号链接到原始文件是具体到Linux,那么所有这些解决方案是Linux特有反正。在stat基于那些需要GNU或busybox的stat。使用最新版本的GNU stat,您可以在fd 0上stat -执行a fstat(),然后在非Linux系统上运行。
斯特凡Chazelas

@Stéphane最后一部分正是OP遇到的情况,这就是为什么我写了两个解决方案的原因,以区别于/dev/null空设备。
斯蒂芬·基特

糟糕,错过了。
斯特凡Chazelas

3

可移植地,检查stdin是null设备(是否打开/dev/null(如的副本/dev/null)),并带有zsh(其stat内置方式比GNU和FreeBSD都早stat(虽然不是IRIX'))):

zmodload zsh/stat
if [ "$(stat +rdev -f 0)" = "$(stat +rdev /dev/null)" ]; then
  echo stdin is open on the null device
fi

(请注意,它没有说明文件描述符是在只读,仅写还是在读+写模式下打开的)。

要检查它是否仅在当前/dev/null文件(/some/chroot/dev/null例如,不是)上打开(仅在Linux上)(在其中,它/dev/stdin是对文件在fd 0上打开的符号链接,而不是在打开时dup(0)在其他系统中类似于的特殊设备):

if [ /dev/stdin -ef /dev/null ]; then
  echo stdin is open on /dev/null
fi

在非Linux上,您可以尝试:

if sh -c 'lsof -tad0 -p"$$" /dev/null' > /dev/null 2>&-; then
  echo stdin is open on /dev/null
fi
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.