Consider
$ zzz > >(echo fine) 2> >(echo error >&2)
fine
error
I was expecting this to keep printing 'error' to terminal because this is what I think is happening here:
First set up all the redirections:
- redirect stdout to >(echo fine) process
- redirect stderr to >(echo error >&2) process
After setting up all the redirections, execute the zzz command:
- since zzz is an invalid command this is redirected to >(echo error >&2) process
echo error >&2
is redirected to stderr- but stderr is redirected to >(echo error >&2) so there is recursion happening here?
At least I didn't expect it to output fine
because zzz command is an invalid command so it won't trigger redirection to stdout and >(echo error >&2)
shouldn't trigger redirection to stdout.
My understanding of this is incomplete/wrong.
Could you explain 1) why recursion doesn't happen and 2) why fine
is printed?
CodePudding user response:
Figured it out.
Let's start with
$ > >(echo fine) 2> >(echo error)
fine
Here the effect is the same as echo error | echo fine
.
Next
$ > >(echo fine) 2> >(echo error >&2)
fine
error
Here the effect is the same as echo fine; echo error >&2
. Because they are disjointed (none of them depend on the other), they are not piped. There is no recursion because echo error >&2
just prints to terminal.
If we try the other way around
$ 2> >(echo error) > >(echo fine >&2)
error
This is the same as { echo fine >&2; } 3>&1 1>&2 2>&3 | echo error
.
If they are disjointed
$ 2> >(echo error) > >(echo fine)
error
fine
This is the same as echo error; echo fine
.
What if we include a command to be run?
$ whoami > >(cat) 2> >(echo error)
error
logan
This is the same as { echo error & whoami; } | cat
.
Another example
$ whoami > >(sed 's/^/processed: /') 2> >(echo error)
processed: error
processed: logan
You could think of this as { echo error & whoami; } | sed 's/^/processed: /'
What if they are disjointed?
$ whoami > >(sed 's/^/processed: /') 2> >(echo error >&2)
error
processed: logan
This is the same as echo error >&2; whoami | sed 's/^/processed: /'
.
Let's try with an invalid command.
$ BOB 2> >(cat) > >(echo hi >&2)
hi
BOB: command not found
This is the same as { echo hi >&2 & BOB; } 3>&1 1>&2 2>&3 | cat
.
What if we want to introduce a custom file descriptor?
$ whoami > >(sed 's/^/processed: /') 3>&1 2> >(echo error >&3)
processed: error
processed: logan
This is the same as { echo error & whoami; } | sed 's/^/processed: /'
. Here be careful that the order of redirections matter!
What if we mix valid and invalid commands in a substituted process?
$ > >(echo fine) 2> >(echo error >&2) 2> >(ls;whoami;echo bob >&2)
fine
error
Here we have preset stderr to print 'error' to terminal. So ls;whoami
portion ends up printing 'fine' to terminal, and echo bob >&2
portion ends up printing 'error'.
Also consider
$> >(echo fine) 2> >(ls;whoami;BOB)
fine
BOB: command not found
Here the difference is that stderr still points to the terminal. So the BOB: command not found error message is just sent to the terminal unaltered.
Also consider
$ BOB > >(echo fine) 2> >(echo error >&2) 2> >(ls;whoami;echo bob >&2)
fine
error
This has the same effect as { ls & whoami & } | echo fine; { echo bob >&2 & BOB; } 3>&1 1>&2 2>&3 | echo error >&2
.