What's the difference between ">&1" and ">/proc/self/fd/1" redirection?

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 calls dup() on the file descriptor, and for >/proc/self/fd/N it just tries to open it as an ordinary file with open(). Apparently proc 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 a dup, I couldn't tell.

$ ls -l /proc/self/fd/3
lrwx------ 1 itvirta itvirta 64 Jul 14 18:24 /proc/self/fd/3 -> socket:[168157]
$ strace bash -c "echo foo >>/proc/self/fd/3" 2>&1 | grep open.*proc/self
open("/proc/self/fd/3", O_WRONLY|O_CREAT|O_APPEND, 0666) = -1 ENXIO (No such device or address)

Also this answer has some information about the portability of /proc/NNN/fd


A redirection like 3>&1 duplicates an existing file descriptor: this takes the same open file (same file, same flags, same position, etc.) and plugs it onto another “output port” of the program (another file descriptor number). (More precisely, this creates a new file descriptor that points to the same file description, but we don't)

>&1 duplicates the descriptor onto itself, some shells optimize it away altogether.

A redirection like >/proc/$pid/fd/1 opens the file /proc/$pid/fd/1. This creates a fresh file descriptor. The files in /proc/*/fd are special, and opening them mostly copies the data from an existing file descriptor. Although the files are symbolic links, they are “magic”; for example, a deleted file or a pipe appears as a broken symlink but can still be opened as if it was an existing file because the kernel contains special code to handle /proc/*/fd entries. So in most cases, >&1 and >/proc/self/fd/1 are equivalent. However, sockets get different treatment.

/*
 *      In theory you can't get an open on this inode, but /proc provides
 *      a back door. Remember to keep it shut otherwise you'll let the
 *      creepy crawlies in.
 */

static int sock_no_open(struct inode *irrelevant, struct file *dontcare)
{
        return -ENXIO;
}

(The code has been reorganized in recent versions, but sockets still can't be opened even via /proc/PID/fd/NUM.)

The reason you can't open a socket like most other file types is that there's more information attached to a socket, you have to say how you open it. For example, opening a TCP socket allocates a source port. Although it might make sense in some contexts, the Linux kernel doesn't allow opening another process's sockets. In the case where the process is the same (/proc/self/fd/NUM, as opposed to /proc/OTHERPID/fd/NUM), it could convert the open call to the usual file descriptor duplication, but opening /proc/self/fd is an unusual thing to do in the first place, and redirection with sockets isn't normally done since it normally doesn't work, so the kernel has not been designed to support this sensible-but-useless exception.