将bash重定向到/dev/stdout:不是一个目录

4
我最近从CentOS 5.8(带有GNU bash 3.2.25)升级到CentOS 6.5(带有GNU bash 4.1.2)。一个在CentOS 5.8下工作的命令,在CentOS 6.5下不再工作。这只是一个简单愚蠢的例子,但我正在尝试理解在bash中发生了什么导致了不同的行为。也许这是bash 4.1.2中的新bug,或者是旧bug在修复后产生的新行为?

CentOS 5.8:

    (echo "hi" > /dev/stdout) > test.txt
    echo $?
0
    cat test.txt
hi

CentOS 6.5:

    (echo "hi" > /dev/stdout) > test.txt
-bash: /dev/stdout: Not a directory
    echo $?
1

更新:看起来这个问题与 CentOS 版本无关。我有另一台 CentOS 6.5 的机器,该命令可以正常工作。我已经排除了任何环境变量的因素。你有什么想法吗? 在所有的机器上,这些命令都给出相同的输出结果:
    ls -ld /dev/stdout
lrwxrwxrwx 1 root root 15 Apr 30 13:30 /dev/stdout -> /proc/self/fd/1

    ls -lL /dev/stdout
crw--w---- 1 user1 tty 136, 0 Oct 28 23:21 /dev/stdout

另一个更新:看起来子shell正在继承父shell的重定向标准输出。我想这并不令人惊讶,但为什么它在一台机器上可以工作,在另一台机器上失败,当它们运行相同的bash版本时呢?
在正常工作的机器上:
    ((ls -la /dev/stdout; ls -la /proc/self/fd/1) >/dev/stdout) > test.txt
    cat test.txt
lrwxrwxrwx 1 root root 15 Aug 13 08:14 /dev/stdout -> /proc/self/fd/1
l-wx------ 1 user1 aladdin 64 Oct 29 06:54 /proc/self/fd/1 -> /home/user1/test.txt

我认为Yu Huang是正确的,重定向到/tmp对两台机器都有效。这两台机器都使用isilon NAS来挂载/home,但可能有些微不同的文件系统版本或配置导致了错误。总之,除非您知道父进程不会将其重定向,否则应避免重定向到/dev/stdout。
更新:此问题出现在从v3升级到NFS v4后。退回到v3后,此行为消失了。

为什么不直接这样做:echo "hi" > test.txt - anubhava
2
@user1999165:它适用于RHEL 5和6。发布ls -ld /dev/stdout的输出。 - Cyrus
@JonathanLeffler 它说 /dev/stdout 不是一个目录,这是正确的,但似乎不相关。无论如何,(echo "hi" > /dev/stdout) > test.txt 应该做什么呢?同时输出到 /dev/stdouttest.txt 吗?我认为这就是 tee 的作用。 - ooga
@ooga:如果问题是/dev/stdout,它会显示“没有这个文件或目录”(或者如果/dev/stdout神秘地成为了一个目录,则会显示“是一个目录”);“不是目录”意味着路径上的某个元素不是目录,而对于可能出现此问题的路径上的唯一元素是//dev - 尽管我不认为我相信它。(echo "hi" > /dev/stdout)将子shell的echo输出重定向到标准输出,这就是它本来要去的地方;> test.txt将文件test.txt的标准输出发送出去。这一切都有点奇怪。 - Jonathan Leffler
我很不想这么说,但你重启过了吗?你现在看到的东西还没有什么意义。我猜可能是Bash设置的问题,但可能性很小。你确认每台机器上都有相同版本的Bash吗? - Jonathan Leffler
显示剩余11条评论
3个回答

2

早上好,user1999165, :)

我猜测这与底层文件系统有关。在同一台机器上,请尝试:

(echo "hi" > /dev/stdout) > /tmp/test.txt

/tmp/ 应该是 Linux 本地文件系统(ext3或其他)。


1
在许多Linux系统上,/dev/stdout是当前进程文件描述符1的别名(链接或类似)。从C语言角度来看,全局stdout与文件描述符1相连。
这意味着echo foo > /dev/stdoutecho foo 1>&1或将文件描述符重定向到自身是相同的。我不会期望它能够工作,因为语义是“关闭描述符以重定向,然后克隆新目标”。所以要使其工作,必须有特殊代码注意到这两个文件描述符实际上是相同的,并跳过“关闭”步骤。
我猜测,在失败的系统上,BASH无法确定/dev/stdout == fd1并实际上将其关闭。虽然错误消息很奇怪,但我不知道任何其他常见的错误可以更好地适合此情况。
注意:我尝试在带有BASH 4.3.11的Kubuntu 14.04上复制您的问题,这里重定向有效(即我没有收到错误)。也许这是修复了的BASH 4.1中的一个错误。

0

我在编写将piped stdin输入写入AWS EFS(NFSV4)时遇到了问题,与此问题相似。(使用Centos 6.8,因此无法升级bash到4.2)。

我向AWS支持团队咨询了此事,以下是他们的回复--

这个问题与EFS本身无关,问题出在bash上。这个问题在RHEL中的bash 4.2或更高版本中已经修复。

为了避免这个问题,请在子shell中运行echo命令之前创建一个文件句柄,然后可以将同一文件句柄用作重定向。像下面的例子:

exec 5> test.txt; (echo "hi" >&5); cat test.txt
hi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接