$ 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)
, каждая команда находится внутри другой оболочки, поэтому они должны иметь одинаковый вывод. Где я не прав?
решение1
Подстановка процесса >(thing)
будет заменена именем файла. Это имя файла соответствует файлу, который подключен к стандартному вводу thing
внутри подстановки.
Следующий пример будет лучшим примером его использования:
$ sort -o >(cat -n >/tmp/out) ~/.profile
Это отсортирует файл ~/.profile
и отправит вывод, в cat -n
который будут перечислены строки и сохранен результат в /tmp/out
.
Итак, отвечая на ваш вопрос: вы получаете этот вывод, потому что echo
получает два аргумента 123
и /dev/fd/63
. /dev/fd/63
— это файл, подключенный к стандартному вводу процесса cat
в подстановке процесса.
Немного изменю ваш пример кода:
$ echo 101 > >(cat)
Это приведет только 101
к стандартному выводу (вывод echo
будет перенаправлен в файл, который служит входом для cat
, и cat
выведет содержимое этого файла на стандартный вывод).
Также обратите внимание, что в cmd1 | cmd2
конвейере cmd2
может вообще не работать в той же оболочке, что и cmd1
(в зависимости от реализации оболочки, которую вы используете). ksh93
работает так, как вы описываете (та же оболочка), при этом bash
создает подоболочку для cmd2
(если только не задана ее lastpipe
опция оболочки и не активировано управление заданиями).
решение2
Для полноты картины
cmd1 >(cmd2)
в основном то же самое, что и
cmd1 | cmd2
в yash
оболочке, и только в этой оболочке.
В этой оболочке >(cmd)
происходит процессперенаправлениев отличие от >(cmd)
/ ksh
/ bash
который zsh
является процессомзамена.
Это не строго эквивалентно, поскольку в cmd1 >(cmd2)
, yash
не ждет 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)
решение3
Потому чтовот что делает замена процесса, он заставляет команду внутри подстановки выглядеть как имя файла. Внутри он соединяет команды через канал, давая путь /dev/fd/NN
к основной команде, так что он может открыть уже открытый дескриптор файла в канале.
Это не то же самое, что и канал. Каналы подключаются stdout
без stdin
использования чего-либо, что выглядит как имена файлов. Подстановка процессов более гибка в том смысле, что вы можете иметь несколько таких подстановок в одной командной строке, но она требует основной команды для открытия файлов по имени (например, cat
а не echo
).
Вы можете эмулировать канал с заменой процессов, выполнив следующие действия:
echo foo > >(cat -n)
решение4
$ 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, i.e. /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)
$