Linux command substitution order
The genesis of the issue you are seeing is due to the way tokenization happens of a command line. In this particular case, much before the $(echo 'echo | tac')
is expanded, the shell has already figured out that you intend to run a command (echo {…}
) and pass it's output via a pipe (|
) to another command (xargs -n 2 $(…)
).
Then in the next stage does the filling in of $(…)
and brace expansion {…}
happens to generate the actual commands to be run. And in this stage if it results in a pipe character, so what, it's too late and it's clearly missed the bus.
Now it will be treated as not as a special metacharacter, but will be included in the xargs
command line as just any ordinary (=> non-metachar) char.
Should you want to take another shot at it, then you need to eval
it.
eval "echo {0..9} | xargs -n 2 $(echo 'echo | tac')"
This will output what you expect.
besides Roman said, your commnand should be like this
echo {0..9}| xargs -n 2 | tac
this produces
8 9
6 7
4 5
2 3
0 1
with the pointless $(echo 'echo') this causes the error
PS. For the long answer, |
redirects the output from one command to the next one, so echo {0..9}
produces:
0 1 2 3 4 5 6 7 8 9
xargs -n 2
takes that input and produces
0 1
2 3
4 5
6 7
8 9
and finally tac
takes that input and produces the reverse expresion:
8 9
6 7
4 5
2 3
0 1
I hope this is a bit clearer.
EDIT:
echo {0..9} | xargs -n 2 $(echo 'echo | tac')
+ echo 0 1 2 3 4 5 6 7 8 9
++ echo 'echo | tac'
+ xargs -n 2 echo '|' tac
| tac 0 1
| tac 2 3
| tac 4 5
| tac 6 7
| tac 8 9
This is the command that you are sending to bash with your initial sintax, it's pretty clear what's happening, at the end you are sending xargs -n 2 echo '|' tac