Что именно <() в bash (и = () в zsh)?

34

Мне довольно комфортно с bash, но недавно я попал на замену, которую я не знал.

Что именно <(<command>)в Bash? Как это по сравнению с =(<command>)в Zsh?

Я понимаю, что это как-то связано с дескрипторами файлов по умолчанию. В моем компьютере

echo <()

Returns /proc/self/fd/11, который, как я обнаружил, является копией скрипта STDOUT, но это все еще кажется мне довольно запутанным.

Энрике Барселуш
источник

Ответы:

47

Это называется процессом замещения.

<(list)Синтаксис поддерживается обоими, bashи zsh. Он позволяет передавать выходные данные команды ( list) другой команде, если использование pipe ( |) невозможно. Например, когда команда просто не поддерживает ввод из STDINили вам нужен вывод нескольких команд:

diff <(ls dirA) <(ls dirB)

<(list)соединяет вывод listс файлом в /dev/fd, если это поддерживается системой, в противном случае используется именованный канал (FIFO) (который также зависит от поддержки системой; ни в одном руководстве не говорится, что происходит, если оба механизма не поддерживаются, предположительно, он прерывается с помощью ошибка). Имя файла затем передается в качестве аргумента в командной строке.


zshдополнительно поддерживает =(list)как возможную замену <(list). При =(list)этом временный файл используется вместо файла в /dev/fdFIFO. Его можно использовать в качестве замены, <(list)если программа должна искать в выводе.

В соответствии с руководством по ZSH могут возникнуть и другие проблемы с тем, как <(list)работает:

=Форма полезна в качестве как /dev/fdи названные реализации трубы <(...)имеют свои недостатки. В первом случае некоторые программы могут автоматически закрывать дескриптор файла, о котором идет речь, прежде чем проверять файл в командной строке, особенно если это необходимо по соображениям безопасности, например, когда программа запускает setuid. Во втором случае, если программа фактически не открывает файл, подоболочка, пытающаяся читать или записывать в канал, будет (в типичной реализации, в разных операционных системах может иметь разное поведение) блокироваться навсегда и должна быть явно уничтожена. , В обоих случаях оболочка фактически передает информацию с использованием канала, поэтому программы, которые ожидают lseek (см. Man-страницу lseek(2)) в файле, не будут работать.

Adaephon
источник
Это помогло мне понять, почему MacOS pfctl -f <(echo "pf rules")сказал плохой дескриптор файла. использование zsh и = (echo "pf rules") вместо этого работает.
johnnyB
9

Обратите внимание, это ответ Bash, а не Zsh.

В bash есть случаи, когда вы не можете использовать трубы:

some_command | some_other_command

Поскольку каналы вводят подоболочки для каждого компонента конвейера, при выходе из подоболочек любые побочные эффекты, на которые вы полагались, исчезли бы. Например, этот надуманный пример:

cat file | while read line; do ((count++)); done
echo $count

будет отображать пустую строку, потому что $countпеременная не существует в текущей оболочке.

Подстановка процесса bash позволяет вам избежать этой загадки, позволяя вам читать из вывода "some_command", как если бы вы были из файла

while read line; do ((count++)); done < <(cat file)
# ....................................1.2
echo $count   # the variable *does* exist in the current shell

(1) - нормальное перенаправление ввода. (2) является началом <()синтаксиса замены процесса.

Гленн Джекман
источник
2
= (cmdlist) в zsh имеет почти тот же эффект, что и <(cmdlist) в bash, но создает (и удаляет, когда будет готов) временный файл с выводом cmdlist для перенаправления. Это хорошо, когда поиск потенциально выполняется в программе. <(cmdlist) также известен zsh.
Гомбай Шандор