Bash redirections cheat sheet
catonmat.net
catonmat.net
If I have two streams,
1 ---------- 1'
2 ---------- 2'
and I redirect the 1>2 1 ----
\
2 ---------- 2'
it seems to work okay.But if you have three,
1 ---------- 1'
2 ---------- 2'
3 ---------- 3'
and try "redirecting 1 to 2 then 2 to 3", the natural reading would be 1 -
\
2 -----
\
3 ---------- 3'
But that's not what you get, because you're not "redirecting streams" - you're copying file descriptors. This is partially fixed by informing people to do the operations backwards, but there are corner cases where that breaks down, and "copying file descriptors in the order they appear on the line" never does because that's genuinely what's going on. 1 -> 1'
2 -> 2'
3 -> 3'
foo 1>2 2>3This is "run foo copying fd 2 to 1 and 3 to 2"
which gives
1 -> 2'
2 -> 3'
3 -> 3'I guess what you're explaining is the process that happens as the kernel reserves fd's? And when you start your program there isn't necessarily any guarantee that a particular fd will be available, except for 0,1,2, right?
Edited for clarity:
The way file descriptors work is that the kernel maintains, for each process, a table mapping from numerical file descriptors to structures describing the open file/socket/device associated with that descriptor.
When the shell is setting up to spawn a new process (or subshell, for that matter), it processes the redirections in order, left to right. For redirections of the form "A>&B" this is a call to dup2(B, A), which has the kernel copy the entry in the table at B over the entry at A.
Incidentally, you will note the direction of the symbol doesn't matter when cloning file descriptors: 5>&7 and 5<&7 mean the same thing - you're just copying a file descriptor. It does matter when opening a file, as the file will be opened for reading or writing before before being dup2'ed over the requested descriptor. Having said that, of course use the correct symbol - it's good documentation.
pipe: get two fds connected by a pipe
fork: create a child process
dup2: move the read fd of the pipe to 0 (stdin) in the new process
exec: run the program in the child process
There's some cleanup but that's the gist - popen isn't a syscall (or reasonably close to one), so must necessarily rely on other stuff to get its work done where it actually interfaces with the kernel.
Glenn-Willens-MacBook-Pro:~ gwillen$ exec 3<> /dev/tcp/<website>/80
Glenn-Willens-MacBook-Pro:~ gwillen$ echo "GET / HTTP/1.0" >&3
Glenn-Willens-MacBook-Pro:~ gwillen$ echo "" >&3
Glenn-Willens-MacBook-Pro:~ gwillen$ cat <& 3
HTTP/1.1 200 OK
Date: Mon, 10 Sep 2012 21:43:33 GMT
Server: Apache
Last-Modified: Sun, 06 Sep 2009 18:44:42 GMT
ETag: "30525-0-472ed20537a80"
Accept-Ranges: bytes
Content-Length: 0
Connection: close
Content-Type: text/html
Glenn-Willens-MacBook-Pro:~ gwillen$ cmd <(another_cmd)
to be particularly useful in the past for pushing generated data into something that doesn't accept - as an alias for STDIN. I'm not sure if it's a different way of expressing one of the listed items though; iirc it creates a temporary fifoIt's most useful when you want to diff the result of multiple commands, e.g.: diffing the contents of two directories:
$ diff <(find /first/path | sort) <(find /second/path | sort)In zsh "=(cmd)" does that, but "=" doesn't work in bash. "<(cmd)" works everywhere... why did zsh trick me with "="?
(cmd 2>&1) > file
to redirect stderr and stdout to a file. I always thought the parenthesis were necessary, but according to this I can just do cmd > file 2>&1
It seems very unintuitive, though. Does anyone know the rationale behind ordering of redirects?(That said, I'm going to use 'cmd &> file' that I just learned from now on)
Reading from left to right:
cmd
Run command cmd. File descriptors: {1: /dev/console, 2:/dev/console} > file
Open a file descriptor for "file" and copy it into our first file descriptor (stdout). File descriptors: {1: fopen('file'), 2: /dev/console } 2>&1
Copy the file descriptor stored in 1 (stdout) into 2 (stderr). File descriptors: {1: fopen('file'), 2: fopen('file')}I can't give you an exact rationale, but my old shell scripting books from the 90's use the cmd > file 2>&1 notation. My personal belief is that it was for situations when you would do something like this:
cmd > output.txt 2> errors.txt
(cmd 2>&1)
runs in its own subshell, and copies stderr to stdout. >file
is interpreted in the parent shell, and copies stdout to 'file'.In the second instance, you run a single shell, first copying stdout to 'file', then copying stderr to the (redirected) stdout, that is, 'file'.
Though not limited to just shell redirections, one of the true masters of bash (and zsh) is Larry Peek whose written/given multiple books, articles, and presentations on the topic.
The feature is confusing (it uses virtual files visibly only to bash, and not extant on the filesystem) and overlaps existing functionality (netcat).
Also:
http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=146464
http://lists.debian.org/debian-user/2003/04/msg01591.html
Rc on the other hand is much more clean: http://rc.cat-v.org
For extra credit, try using a "Here Document" as input for 'echo' and 'cat'. Explain the results.
Doing this:
"cat << EOL
line one
line two
EOL"
Would be the same as this:
"cat < file"
That is if 'file' contained this:
"line one
line two"
This doesn't work with the echo command because it does not use stdin like cat does.
You can also search https://www.google.com/search?q=bash and follow links to "Documentation"
xargs echo <<EOL
> line 1
> line 2
> EOL
line 1 line 2
$ cat <<EOL
> line 1
> line 2
> EOL
line 1
line 2
line-terminators get eaten by xargs