echo 123 | cat 123
делает то, что я ожидал, обе команды запускаются внутри одной оболочки.
но когда я соединяю их с
> (…) Выражение
который соединяет вывод одной команды в оболочке со второй в подоболочке, я получаю следующее:
echo 123 >(cat) 123 /dev/fd/63
это также верно и для других значений:
echo 101 >(cat) 101 /dev/fd/63 echo $BASHPID >(cat) 3252 /dev/fd/63
Я думал, что command1> (command2) совпадает с command1 | command2 , но с той разницей, что в command1> (command2) каждая команда находится внутри другой оболочки, поэтому они должны иметь одинаковый вывод, где я ошибаюсь?
Подстановка процесса >(thing)
будет заменена именем файла. Это имя файла соответствует файлу, который связан со стандартным вводом thing
внутри подстановки.
Следующий пример будет лучшим примером его использования:
$ sort -o >(cat -n >/tmp/out) ~/.profile
Это отсортировало файл ~/.profile
и отправил вывод cat -n
который перечислил бы строки и сохранил результат в /tmp/out
.
Итак, чтобы ответить на ваш вопрос: вы получаете этот вывод, потому что echo
получает два аргумента 123
и /dev/fd/63
. /dev/fd/63
– файл, связанный со стандартным входом процесса cat
в процессе замены.
Поскольку это то, что делает подстановка процесса , это делает команду внутри подстановки отображаемой в виде имени файла. Внутренне он соединяет команды через канал, предоставляя путь /dev/fd/NN
к основной команде, поэтому он может открыть уже открытый файловый дескриптор для канала.
Это не то же самое, что и труба. Трубы подключают stdout
к stdin
не затрагивая ничего, что похоже на имена файлов. Подстановка процесса более гибкая, поскольку вы можете иметь несколько таких подстановок в одной командной строке, но для ее основной команды требуется открыть файлы по имени (например, cat
а не echo
).
Вы можете эмулировать трубу с заменой процесса, выполняя что-то вроде этого:
echo foo > >(cat -n)
$ echo 1 >(cat > /dev/null) 1 /dev/fd/63 $ echo echo >(cat /dev/null) echo /dev/fd/63 # We can trace how the commands are executed # so long as we avoid using shell builtin commands, # and run the equivalent external program instead, ie /usr/bin/echo $ strace -f -e execve bash -c '/usr/bin/echo >(cat /dev/null)' execve("/usr/bin/bash", ["bash", "-c", "/usr/bin/echo >(cat /dev/null)"], [/* 56 vars */]) = 0 strace: Process 4213 attached [pid 4212] execve("/usr/bin/echo", ["/usr/bin/echo", "/dev/fd/63"], [/* 56 vars */]) = 0 strace: Process 4214 attached [pid 4214] execve("/usr/bin/cat", ["cat", "/dev/null"], [/* 56 vars */]/dev/fd/63 ) = 0 [pid 4212] +++ exited with 0 +++ [pid 4214] +++ exited with 0 +++ --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=4214, si_uid=1001, si_status=0, si_utime=0, si_stime=0} --- +++ exited with 0 +++ # Apparently, the order of evaluation is arranged so this works nicely: $ echo 1 > >(cat > /dev/null) $
Для полноты
cmd1 >(cmd2)
в основном такой же, как
cmd1 | cmd2
в оболочке yash
и только этой оболочке.
В этой оболочке >(cmd)
есть переадресация процесса, а не >(cmd)
ksh
/ bash
/ zsh
которая является замещением процесса.
Это не строго эквивалентно, потому что в cmd1 >(cmd2)
не ждет cmd2
, поэтому вы можете обнаружить следующее:
$ yash -c 'echo A >(cat); echo B' B A $ yash -c 'echo A | cat; echo B' A B
Напротив, замещение процесса расширяется до пути к файлу (обычно это именованный канал или /dev/fd/<x>
где <x>
– это fd для заранее созданного канала), который при открытии для записи позволит отправить вывод в cmd
.
Хотя подстановка процесса была введена ksh
, в этой оболочке вы не можете передавать их в качестве аргумента в перенаправления.
ksh -c 'cmd1 > >(cmd2)'
для эмулирования перенаправления процесса yash не будет работать. Там вы должны передать это имя файла, полученное в результате замены в качестве аргумента для команды, например:
ksh -c 'diff <(echo a) <(echo b)'
Он будет работать в bash
и zsh
.
Однако в bash
например, для перенаправления процесса yash, оболочка не ждет команды ( cmd2
). Так:
$ bash -c 'echo A > >(cat); echo B' B A
Замена процесса ksh
можно эмулировать с помощью yash
с:
cmd1 /dev/fd/5 5>(cmd2)
Подобно:
diff /dev/fd/3 3<(echo a) /dev/fd/4 4<(echo b)