I'm working on some script that being run by rc.local
at startup, and I noticed that output redirection works quite strange.
If I write something like echo "foo" >&1
, it ends up in syslog, and all is okay.
But when I write echo "foo" >>/dev/stdout
, or echo "foo" >>/proc/self/fd/1
, I got an error saying that there is no such device or address.
Further investigation revealed that /proc/self/fd/1
was in fact a socket.
Including ls -l /proc/self/fd
in rc.local
prints the following:
lr-x------. 1 root root 64 Jul 14 05:28 0 -> /dev/null
lrwx------. 1 root root 64 Jul 14 05:28 1 -> socket:[18485]
lrwx------. 1 root root 64 Jul 14 05:28 2 -> socket:[18485]
lr-x------. 1 root root 64 Jul 14 05:28 255 -> /etc/rc.d/rc.local
The described behaviour can be easily reproduced with nc
. First, find out which file descriptor binds to socket. You can do it by issuing nc -l -p 25566 -c "ls -l /proc/self/fd"
in first terminal and then telnet localhost 25566
in other terminal. In my case it was 5-th descriptor.
Okay. Then, to reproduce the issue, in first terminal:
nc -l -p 25566 -c "echo 'hello' >&5"
in second terminal:
telnet localhost 25566
You shall see "hello" in second terminal output between telnet messages about connection being established and closed.
Now the case with file descriptor pseudo-file from /proc
:
first terminal:
nc -l -p 25566 -c "echo 'hello' >/proc/self/fd/5"
second terminal:
telnet localhost 25566
Now second terminal just contains telnet messages about connection established and immediately closed, and first terminal shows an error: sh: /proc/self/fd/5: No such device or address
.
Edit: OS is Fedora 23 i686 server
So, the question. What's the difference between >&1
and >/proc/self/fd/1
? And is there some universal and reliable way to redirect output to some file, which corresponds exactly to current standard output?
Thank you.
Edit 2:
For clarity, exact input/output for Fedora 23 i686 for above case:
Terminal 1 | Terminal 2
$ nc -l -p 25566 -c 'ls -go /proc/self/fd' |
| $ telnet localhost 25566
| Trying ::1...
| Connected to localhost.
| Escape character is '^]'.
| total 0
| lr-x------ 1 64 Jul 14 08:56 0 -> pipe:[19687]
| l-wx------ 1 64 Jul 14 08:56 1 -> pipe:[19688]
| lrwx------ 1 64 Jul 14 08:56 2 -> /dev/tty2
| lr-x------ 1 64 Jul 14 08:56 3 -> pipe:[19687]
| lr-x------ 1 64 Jul 14 08:56 4 -> /proc/1285/fd
| lrwx------ 1 64 Jul 14 08:56 5 -> socket:[19686]
| l-wx------ 1 64 Jul 14 08:56 7 -> /pipe:[19688]
| Connection closed by foreign host.
$ nc -l -p 25566 -c "echo 'hi' >&5" |
| $ telnet localhost 25566
| Trying ::1...
| Connected to localhost.
| Escape character is '^]'.
| hi
| Connection closed by foreign host.
$ nc -l -p 25566 -c "echo 'hi' >/proc/self/fd/5" |
| $ telnet localhost 25566
| Trying ::1...
| Connected to localhost.
sh: /proc/self/fd/5: No such device or address | Escape character is '^]'.
| Connection closed by foreign host.
$ | $
Best Answer
Use
>&N
. It's portable and as you saw, actually works with sockets.The only reason you would ever use
/proc/self/fd
is you are running a program that expects a file name and can't be told to use an already open file descriptor. E.g. the<(cmd...)
redirection uses that, since almost all command line utilities can open a file pointed to by name, but not all support preopened file descriptors directly.Your shell can use pre-existing file descriptors, though, so no need to go through
/proc
.Also,
/proc/NNN/fd/
is specific to Linux, and requires that you have a mounted/proc
. On my Linux boxes,/dev/stdout
,/dev/fd/*
and others are symlinks to/proc/self/fd/*
etc., so they require/proc
too. On other Unixes, they might be different. According to the answers to an older question/dev/stdout
are specifically listed as outside POSIX.As for why the redirection doesn't work as you tried: Trying out with
strace
, the difference between the two is that for a>&N
redirection,bash
callsdup()
on the file descriptor, and for>/proc/self/fd/N
it just tries to open it as an ordinary file withopen()
. Apparentlyproc
doesn't support opening new copies of sockets like that, so the call fails. Stream sockets are pretty much point to point links, so prohibiting opening of a new copy doesn't seem too unnatural. But why it works for pipes or with adup
, I couldn't tell.Also this answer has some information about the portability of
/proc/NNN/fd