Illustrated Redirection Tutorial
wiki.bash-hackers.org
wiki.bash-hackers.org
I tend to think "a>&b" as an assignment to a stream: stream[a]=stream[b]. This way it's easier to understand why the ordering matters:
>file 2>&1 means stream[1]=file; stream[2]=stream[1];
2>&1 >file means stream[2]=stream[1]; stream[1]=file;>file = redirect stdout to file 2>&1 = redirect stderr to stdout which is being redirect to file
trying to state they = something seems like it would be confusing as they don't equal as much as just being re-routed.
Edit: I've never seen it described as TFA, but would have put money that I have done it in either order and worked without issue. i guess it's a good thing i'm not a gambler
Now the article tells me not to use them, but the justification [1] is a broken link. I assume [2] is the correct link, but the justification there is not convincing to me. It says other shells that don't implement them parse them differently, which is tautologically correct, and is also already the case for every other non-POSIX feature in bash and every other shell. So I'm not sure why these two operators should be treated differently.
POSIX compatibility is a great thing to have available, when you need it. But my personal scripts and one-offs do not need to be POSIX-compatible!
One of the challenges in the LPIC-1 study material I waaay back in 2008 was something like, how do you swap the stdout / stdin around? The solution required the use of a 3rd file descriptor, and an understanding that the order of ops was important.
Edit: let me embarrass myself by fucking this up, from my phone, without checking in a terminal...
run.sh 3>&1 1>&2 2>&3
So:
1. You point fd3 to "the thing that stdout is pointing at" (which keeps a safe copy of stdout during the juggling exercise)
2. Next you point fd1 at "the thing that stderr points to". So phase one is complete: stdout will now show the contents of stderr.
3. Finally you point fd2 at the copy of fd1's target. This completes the task, since stderr will now show the contents of stdout
We don't really care what happens to fd3. But if we did, we could fix that by directing it to /dev/null at the end. Again, showing how important the ordering is.
it's good practice to close extra FDs for children who are not expecting it. poorly designed applications may misbehave if provided with unexpected additional FDs. you shouldn't use /dev/null for this purpose, but instead actually close it with 3>&-.
I learned that parsing here docs is another one of them! It's a weird syntax, and I came up with something clever for parsing it. And then I realized there is a simple algorithm that kind of "falls out" of the AST.
https://www.oilshell.org/blog/2016/10/17.html
The famous "worse is better" essay on Unix talks about implementation simplicity vs. interface simplicity. Unix favors implementation simplicity!
What is dup2()? It's basically an assignment of the things that file descriptors point to. Why not use regular assignment? Because the things they point to live in the kernel's memory, not your own address space :)
Here I describe file descriptors as "pointers" from user space into the kernel. They are integers and not literal pointers because they're in a different address space.
Three Comics For Understanding Unix Shell: https://www.oilshell.org/blog/2020/04/comics.html
So yes redirects are a very low level and somewhat confusing syntax, and you have to understand how the kernel works to really understand them.
----
Although I didn't really understand this until I implemented a shell, and instead I mostly used these canned patterns:
sort < myfile
mycmd 2>&1
mycmd 2>&1 | wc -l # stdout and stderr to pipe
mycmd >file.txt 2>&1 # stdout and stderr to file
echo error >&2
That's about all you need. Related:Avoid Directly Manipulating File Descriptors in Shell: https://www.oilshell.org/blog/2017/08/12.html (all usages of say 4>&5 can be done simply by regular direction of shell FUNCTIONS, no need to juggle them manually)
I would like to offer a small difference of opinion on formatting: I encourage a space after `>` and `<`, before the filename argument. I guess it's subjective, and maybe varies by person, font, syntax coloring, etc., but... I think that both helps the redirection stand out, distinguishes it descriptor duplicating syntax, and avoids reduces punctuation clumped next to each other.
To use one of the author's examples:
cmd arg1 arg2 <myFile 3<&1 2>/dev/null >&2
cmd arg1 arg2 < myFile 3<&1 2> /dev/null >&2
cmd arg1 arg2 \
< myFile \
3<&1 \
2> /dev/null \
>&2
Keep in mind that, very often, the filenames will involve variable expressions (though I couldn't say objectively whether that's an argument for or against the space): foo hello --today dog < "$InputFile" > "${OutputBase}.js"Unfortunately, anyone who needs it is highly unlikely to make it through the first four paragraphs.
echo "my_system_config ${MyVar}" | sudo tee /etc/my-config-file
And with here-documents: sudo tee /etc/my-config-file <<EOH
line one
line ${MyVar}
line three
EOHMy favourite muscle-memory fix to the lack of "sudo vi" muscle memory