clarify some language; insert spaces in args to confuse beginners less (#400)

* clarify some language; insert spaces in args to confuse beginners less

* fix 2>&1 footgun, explain it and how to avoid it

* add cadence and AstroSnail to credits
This commit is contained in:
artemis 2021-09-23 04:17:04 -07:00 committed by GitHub
parent e668250e16
commit dfe84da074
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 51 additions and 19 deletions

View File

@ -60,7 +60,7 @@ use the `cut` command to select that small subset from each line, and you can
feed the `cut` command's standard input using the `<` operator:
```console
$ cut -d' ' -f2 < uname.txt
$ cut -d ' ' -f 2 < uname.txt
shachi
chrysalis
kos-mos
@ -100,7 +100,7 @@ Let's say we want to rewrite that `cut` command above to use pipes. You could
write it like this:
```sh
cat uname.txt | cut -d' ' -f2
cat uname.txt | cut -d ' ' -f 2
```
[The mnemonic we use for remembering the `cut` command is that fields are
@ -110,7 +110,7 @@ separated by the `d`elimiter and you cut out the nth
This will get you the exact same output:
```console
$ cat uname.txt | cut -d' ' -f2
$ cat uname.txt | cut -d ' ' -f 2
shachi
chrysalis
kos-mos
@ -123,7 +123,7 @@ easier to tack on more specific selectors or operations as you go along. For
example, if you wanted to sort them you could pipe the result to `sort`:
```console
$ cat uname.txt | cut -d' ' -f2 | sort
$ cat uname.txt | cut -d ' ' -f 2 | sort
chrysalis
kos-mos
ontos
@ -224,24 +224,52 @@ another. Let's say you have a need for both standard out and standard error to
go to the same file. You can do this with a command like this:
```
$ rustc foo.rs 2>&1 > foo.log
$ rustc foo.rs > foo.log 2>&1
```
This tells the shell to point standard error to standard out and then the
combined output to `foo.log`. There's a short form of this too:
This tells the shell to point standard out to `foo.log`, and then standard
error to standard out (which is now `foo.log`). There's a footgun here though;
the order of the redirects matters. Consider the following:
```
$ rustc foo.rs 2>&1 > foo.log
error: expected one of `!` or `::`, found `main`
--> foo.rs:1:5
|
1 | fun main() {}
| ^^^^ expected one of `!` or `::`
error: aborting due to previous error
$ cat foo.log
$ # foo.log is empty, why???
```
We wanted to redirect stderr to `foo.log`, but that didn't happen. Why? Well,
the shell considers our redirects one at a time from left to right. When the
shell sees `2>&1`, it hasn't considered `> foo.log` yet, so standard out (`1`)
is still our terminal. It dutifully redirects stderr to the terminal, which is
where it was already going anyway. Then it sees `1 > foo.log`, so it redirects
standard out to `foo.log`. That's the end of it though. It doesn't
retroactively redirect standard error to match the new standard out, so our
errors get dumped to our terminal instead of the file.
Confusing right? Lucky for us, there's a short form that redirects both at the
same time, making this mistake impossible:
```
$ rustc foo.rs &> foo.log
```
[Where can I expect to use that?](conversation://Mara/hmm)
This will put standard out and standard error to `foo.log` the same way that
`> foo.log 2>&1` will.
[It's a bourne shell extension, but I've tested it in `zsh` and `fish`. You can
also do `&|` to pipe both standard out and standard error at the same time in
the same way you'd do `2>&1 | whatever`.](conversation://Cadey/enby)
[Will that work in every shell?](conversation://Mara/hmm)
That will put standard out and standard error to `foo.log` the same way that
`2>&1 > foo.log` will. You can also use this with `>>`:
[It's a bourne shell (`bash`) extension, but I've tested it in `zsh` and `fish`.
You can also do `&|` to pipe both standard out and standard error at the same
time in the same way you'd do `2>&1 | whatever`.](conversation://Cadey/enby)
You can also use this with `>>`:
```
$ rustc foo.rs &>> foo.log
@ -265,9 +293,13 @@ error: aborting due to previous error
[How do I redirect standard in to a file?](conversation://Mara/hmm)
The answer there is not directly! There is a workaround in the form of a tool
called `tee` which outputs its standard in to both standard out and a file. For
example:
Well, you don't. Standard in is an input, so you can change where it comes
_from_, not where it goes.
But, maybe you want to make a copy of a program's input and send it somewhere
else. There is a way to do _that_ using a command called `tee`. `tee` copies
its standard input to standard output, but it also writes a second copy to a
file. For example:
```console
$ dmesg | tee dmesg.txt | grep 'msedge'
@ -345,6 +377,6 @@ What else could you do with pipes and redirection? The cloud's the limit!
---
Thanks to violet spark for looking over this post and fact-checking as well as
helping mend some of the brain dump and awkward wording into more polished
sentences.
Thanks to violet spark, cadence, and AstroSnail for looking over this post and
fact-checking as well as helping mend some of the brain dump and awkward
wording into more polished sentences.