Home > Enterprise >  Bash redirecting a substituted process that redirects back to itself
Bash redirecting a substituted process that redirects back to itself

Time:07-21

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.

  • Related