23

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

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

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

echo <()

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

2 ответа2

32

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

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

diff <(ls dirA) <(ls dirB)

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


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

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

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

8

Обратите внимание, это ответ 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) является началом синтаксиса замены процесса <() .

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .