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

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

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

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

echo <()

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

3 ответа

Решение

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

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

diff <(ls dirA) <(ls dirB)

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


zsh дополнительно поддерживает =(list) как возможная замена <(list), С =(list) временный файл используется вместо файла в /dev/fd или ФИФО. Может использоваться как замена <(list) если программа должна искать в выводе.

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

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

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

Еще одно различие между=(command)&<(command)в zsh синхронное и асинхронное выполнение:

      date & \
diff =(sleep 4; date) =(sleep 5; date) & \
diff <(sleep 4; date) <(sleep 5; date) &
      Sun 29 Nov 2020 08:16:01 AEDT
[1]    41717 done       date
      $ 1c1
< Sun 29 Nov 2020 08:16:05 AEDT
---
> Sun 29 Nov 2020 08:16:06 AEDT

[3]  + 41719 exit 1     diff <(sleep 4; date) <(sleep 5; date)
      $ 1c1
< Sun 29 Nov 2020 08:16:05 AEDT
---
> Sun 29 Nov 2020 08:16:10 AEDT

[2]  + 41718 exit 1     diff =(sleep 4; date) =(sleep 5; date)

... хотя понятия не имею, было ли это задумано или нет, поскольку я не могу придумать веской причины для этого=()функционировать синхронно.

Другие вопросы по тегам