4

I recently upgraded from CentOS 5.8 (with GNU bash 3.2.25) to CentOS 6.5 (with GNU bash 4.1.2). A command that used to work with CentOS 5.8 no longer works with CentOS 6.5. It is a silly example with an easy workaround, but I am trying to understand what is going on underneath the bash hood that is causing the different behavior. Maybe it is a new bug in bash 4.1.2 or an old bug that was fixed and the new behavior is expected?

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

Update: It doesn't look like this is problem related to CentOS version. I have another CentOS 6.5 machine where the command works. I have eliminated any environment variables as the culprit. Any ideas? On all the machines these commands gives the same output:

    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

Another Update: It seems the sub-shell is inheriting the redirected stdout of the parent shell. The is not too surprising I guess, but still why does it work on one machine, but fail on the other machine when they are running the same bash version?

On the working machine:

    ((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

I think Yu Huang is right, redirecting to /tmp works on both machines. Both machines are using isilon NAS for the /home mount, but probably one has slightly different filesystem version or configuration that caused the error. In conclusion, redirecting to /dev/stdout should be avoided unless you know the parent process will not redirecting it.

UPDATE: This problem arose after upgrade to NFS v4 from v3. After downgrading back to v3 this behavior went away.

Eric Roller
  • 429
  • 5
  • 19
  • Why not simply do: `echo "hi" > test.txt`? – anubhava Oct 29 '14 at 06:00
  • 2
    @user1999165: It works with RHEL 5 and 6. Post output of `ls -ld /dev/stdout`. – Cyrus Oct 29 '14 at 06:01
  • That's a very curious error message. Superficially, it means that `/dev` is not a directory, but that is very, very improbable. – Jonathan Leffler Oct 29 '14 at 06:06
  • @JonathanLeffler It's saying that `/dev/stdout` is not a directory, which is true but which doesn't seem relevant. At any rate, what is `(echo "hi" > /dev/stdout) > test.txt` supposed to do, anyway? Output to `/dev/stdout` and `test.txt` at the same time? I thought that's what `tee` was for. – ooga Oct 29 '14 at 06:09
  • @ooga: It would say "No such file or directory" if it was `/dev/stdout` that was the problem (or "Is a directory" is `/dev/stdout` was mysteriously a directory); the "Not a directory" means that an element on the path is not a directory, and the only elements on the path for which that can be a problem are `/` and `/dev` — though I don't think I believe it. The `(echo "hi" > /dev/stdout)` redirects the output of the sub-shell's `echo` to standard output, which is where it was going to go anyway; the `> test.txt` sends the standard output of the file `test.txt`. It's all just a tad weird. – Jonathan Leffler Oct 29 '14 at 06:13
  • @JonathanLeffler Oh, I see. If it's the last component of the path that's missing, it could be a file or directory. But if it's a middle component, then it must be a directory. Got it. Still, very curious. – ooga Oct 29 '14 at 06:16
  • Is the `/proc` file system mounted on the machine where it is failing? What do you get if you use `ls -lL /dev/stdout`? – Jonathan Leffler Oct 29 '14 at 06:17
  • @user1999165: Output of `ls -ld /dev/stdout` looks okay compared to RHEL 5 and 6. – Cyrus Oct 29 '14 at 06:20
  • @Jonathan Leffler yes /proc is mounted. See question update for output of ls -lL /dev/stdout – Eric Roller Oct 29 '14 at 06:25
  • Now I am even more puzzled. If the `ls -lL` had generated an error, all would have been explicable — it would have been another "Not a directory" error and some part of `/proc/self/fd` would not be a directory. Clutching at straws time; what happens with `(sleep 1; echo "Hi" >/dev/stdout) > test.txt`? Theory: maybe the value of `/proc/self/fd/1` is changing too fast. Quite honestly, I'd be fairly astonished if this is the issue, but it is worth the try. Also, perhaps, `(ls -lL /dev/stdout; echo Hi >/dev/stdout) > text.txt` might be interesting. – Jonathan Leffler Oct 29 '14 at 06:30
  • @JonathanLeffler No luck. Same error. Since two machines with the same bash version are behaving differently could there be some bash settings I am not seeing. The env are clean on both. – Eric Roller Oct 29 '14 at 06:37
  • I hate to say this, but have you rebooted? What you're seeing doesn't make much sense yet. It might, I suppose, be a Bash setting, though it is pretty unlikely. You have checked that you have the same Bash version on each machine? – Jonathan Leffler Oct 29 '14 at 06:41
  • I don't have admin to reboot but there are multiple machines in a cluster that all experience this behavior. A separate cluster of machines with the same CentOS version and same bash version works fine. I'll let this marinate on stackoverflow for a while. Ultimately it is not a big deal since there are many workarounds, but I just found it odd and was curious – Eric Roller Oct 29 '14 at 06:51
  • The different `pts` links are to be expected, that's how they are set up when you log in. – tripleee Oct 29 '14 at 08:05
  • @tripleee thanks for confirming that. I see the different pts are setup per user shell – Eric Roller Oct 29 '14 at 13:58
  • According to [the bash manual](http://www.gnu.org/software/bash/manual/html_node/Redirections.html), bash treats the name `"/dev/stdout"` specially in redirections. – Keith Thompson Nov 10 '14 at 18:56

3 Answers3

2

Good morning, user1999165, :)

I suspect it's related to the underlying filesystem. On the same machine, try:

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

/tmp/ should be linux native (ext3 or something) filesystem

Yu Huang
  • 106
  • 1
  • 2
1

On many Linux systems, /dev/stdout is an alias (link or similar) for file descriptor 1 of the current process. When you look at it from C, then the global stdout is connected to file descriptor 1.

That means echo foo > /dev/stdout is the same as echo foo 1>&1 or a redirect of a file descriptor to itself. I wouldn't expect this to work since the semantics are "close descriptor to redirect and then clone the new target". So to make it work, there must be special code which notices that the two file descriptors are actually the same and which skips the "close" step.

My guess is that on the system where it fails, BASH isn't able to figure out /dev/stdout == fd1 and actually closes it. The error message is weird, though. OTOH, I don't know any other common error which would fit better.

Note: I tried to replicate your problem on Kubuntu 14.04 with BASH 4.3.11 and here, the redirect works (i.e. I don't get an error). Maybe it's a bug in BASH 4.1 which was fixed, since.

Aaron Digulla
  • 321,842
  • 108
  • 597
  • 820
0

I was seeing issues writing piped stdin input to AWS EFS (NFSV4) that paralleled this issue. (Using Centos 6.8 so unfortunately cannot upgrade bash to 4.2).

I asked AWS support about this, here's their response --

This problem is not related to EFS itself, the problem here is with bash. This issue was fixed in bash 4.2 or later in RHEL.

To avoid this problem, please, try to create a file handle before running the echo command within a subshell, after that the same file handler can be used as a redirect. Like the below example:

exec 5> test.txt; (echo "hi" >&5); cat test.txt
hi
storm_m2138
  • 2,281
  • 2
  • 20
  • 18