There are a couple of problems with the whole line that we need to analyze, that is:
seq 2 | tee >(sed 's/^/e/' > /dev/stderr) | paste /dev/stdin /dev/stderr
stderr
First, the last command. Only stdout can pass through a pipe:
$ seq2 | paste -
1
2
$ seq2 | paste - -
1 2
There is nothing to read from stderr
:
$ seq 2 | paste - /dev/stderr
1 ^C
You need to ^C
it because it blocks, there is nothing to read from stderr
.
Even if you create some output to stderr
it doesn't travel through a pipe:
$ { seq 2; seq 3 4 >/dev/stderr; } | paste - /dev/stderr
1 3
4
Exactly as before, the 1
gets printed and the paste
blocks waiting for stderr
.
The other 2 numbers went directly to the console and got (independently) printed.
You could give some input to stderr
in the last command of the pipe:
$ { seq 2; seq 3 4 >/dev/stderr; } | paste - /dev/stderr 2</dev/null
1
2
3
4
Which is exactly the same as 2>/dev/null
by the way, to avoid blocking the second file descriptor used in the paste
command. But the values printed come directly from the seq 3 4
redirected to the console, not from paste
. This does the same:
$ { seq 2; seq 3 4 >/dev/tty; } | paste - /dev/stderr 2</dev/null
1
2
3
4
And this doesn't block:
$ seq 2 | tee >(sed 's/^/e/' > /dev/stderr) |
paste /dev/stdin /dev/stderr 2</dev/null
1
2
e1
e2
order
Second, the output of tee
doesn't have to be "in order". `tee` and `bash` process substitution order
And, in fact: The output of a process substitution doesn't have to be "in order":
The process substitution output is out of the order
$ echo one; echo two > >(cat); echo three;
one
three
two
In fact, on some examples, if you try several times, you could get different orders. non-deterministic output from independent processes run concurrently by process substitution
$ printf '%s\n' {0..1000} | tee >(head -n2) >(sort -grk1,1 | head -n3) >/dev/null
1000
999
998
0
1
So, no, it could not be done with process substitution and paste.
You need to give some order to the execution:
$ seq 2 | { while read a; do printf "%s %s\n" "$a" "e$a" ; done; }
1 e1
2 e2
bb
So, your bb function, which (basically) contains:
| tee >(sed 's/^/e/')
Which could be tested with :
$ printf '%s\n' {0..1000} | tee >(sort -grk1,1 | head -n3 >&2) | head -n 2
0
1
291
290
289
Should print 0, 1, 1000, 999, 998, In that order, but many times it doesn't.
That is: It is intrinsically in-stable.
Stable real Solution.
The only safe solution for bb is to avoid any process substitution.
And, taking advantage that the {…}
capture both stdout and stderr, example:
$ bash -c '{ echo test-str >/dev/stderr; }' 2>/dev/null
No output, remove the 2 to confirm.
This will work for bb:
$ bb() { seq 5 | tee /dev/stderr | sed 's/^/e/'; }
And use a fifo for paste:
$ mkfifo out2
$ bb 2>out2 | paste out2 -
1 e1
2 e2
3 e3
4 e4
5 e5
You will need to set a trap to remove the fifo file and test if the fifo file exist before creating it.
Seems to work portably on all shells (compatible with Almquist syntax) I tested. Not fully tested, ask for confirmation from other users, there may be some yet unknown surprises.
paste /dev/stderr
does) reads what you type on the keyboard, writing to it (like yourtee /dev/stderr
does) sends it for display by the terminal emulator. – Stéphane Chazelas Oct 09 '18 at 12:34for i in $(seq 2); do echo -n "$i " > /dev/stdout; echo e$i > /dev/stderr; done
and you will get the same effect – mrc02_kr Oct 09 '18 at 12:39paste
or not possible? A negative answer would be just as useful. – agc Oct 09 '18 at 13:36