2

I already know how to use tee with process substitution to send output to various commands, and stdout, eg

command0 | tee >(command1) >(command2)

With the above line, stdout will be composed of interleaved lines from command0, command1, and command2.

Is there a way to prevent tee from writing to stdout, without removing the output of any commands it pipes to? So for the example above, for stdout to only have output from command1 and command2? Most answers relating to teeing without stdout are only writing directly to files, and recommend using something like this:

command0 | tee file1 file2 >/dev/null

But with process substitution, that would consume all output from the other commands too.

command0 | tee >(command1) >(command2) >/dev/null

Is there some way to tell tee not to print to stdout, or to only consume the output directly from tee?

Zoey Hewll
  • 4,788
  • 2
  • 20
  • 33

2 Answers2

3

Try this:

( command0 | tee >(command1 1>&3 ) | command2 ) 3>&1

It redirects the stdout of command1 to pipe 3, so that command2 sees only the original source. At end, you redirect pipe 3 to stdout again.

Use this to test it:

( echo test | tee >( sed 's/^/1 /' >&3 )  | sed 's/^/2 /' ) 3>&1

The output is unordered and in my case:

2 test
1 test
Wiimm
  • 2,971
  • 1
  • 15
  • 25
1

I have seen a comment and an answer that use an extra >, but don't really explain why it does what it does. It seems like it is redirecting output somewhere but all I can tell so far is that it does what I'm looking for. This works:

command0 | tee > >(command1) >(command2)
command0 | tee >(command1) > >(command2)

it appears not to matter where the extra > is, so long as it is before at least one of the arguments to tee. So this will not work:

command0 | tee >(command1) >(command2) >

Without knowing what this is called, and with no further leads, I can't explain further.

Zoey Hewll
  • 4,788
  • 2
  • 20
  • 33
  • `> >(foo)` redirects to the [*process substitution*](http://gnu.org/software/bash/manual/bash.html#Process-Substitution) `>(foo)`. Process substitution involves replacing the `>(foo)` with the path to a file representing `foo`'s stdin, so think of it as `tee > /some/stdin1 /some/stdin2`, or equivalently, `tee /some/stdin2 > /some/stdin1`, which should make it clear what's happening. – muru Apr 05 '19 at 05:57
  • That makes me think that it would send an additional copy of the piped output to the process, but I know empirically that that isn't the case. The linked documentation doesn't seem to mention this pattern anywhere either. – Zoey Hewll Apr 05 '19 at 06:29
  • Oh, are you saying that `>(foo)` is no longer an argument to tee in the case of `> >(foo)`, but just a redirection in the middle of the command? – Zoey Hewll Apr 05 '19 at 06:32
  • 1
    Yes, `> >(foo)` is a redirection in the middle of the command. Just think of `>(foo)` as a path to a file (a special file, but a file nonetheless). – muru Apr 05 '19 at 06:38
  • I just realised, I'm not sure how I came to the conclusion that `>/dev/null` fails in this case, because it doesn't. It only captures the output from `tee`'s stdout, and not the processes it sends output to. – Zoey Hewll Apr 05 '19 at 06:43