Почему в выводе «echo 123 >(cat)» присутствует «/dev/fd/63»?

Почему в выводе «echo 123 >(cat)» присутствует «/dev/fd/63»?
$ 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)
$

Связанный контент