O que exatamente é <() no bash (e = () no zsh)?

36

Estou bastante confortável com o bash, mas recentemente acabei em uma substituição que não conhecia.

O que exatamente está <(<command>)no bash? Como ele se compara ao =(<command>)zsh?

Eu entendo que isso tem algo a ver com descritores de arquivo padrão. No meu computador

echo <()

retorna /proc/self/fd/11, que eu achei uma cópia do script STDOUT, mas isso ainda me parece bastante confuso.

Henrique Barcelos
fonte

Respostas:

51

Isso é chamado de substituição de processo.

A <(list)sintaxe é suportada por ambos, bashe zsh. Ele fornece uma maneira de passar a saída de um comando ( list) para outro comando quando o uso de um pipe ( |) não é possível. Por exemplo, quando um comando simplesmente não suporta entrada de STDINou você precisa da saída de vários comandos:

diff <(ls dirA) <(ls dirB)

<(list)conecta a saída de listcom um arquivo em /dev/fd, se suportado pelo sistema, caso contrário, um pipe nomeado (FIFO) é usado (que também depende do suporte do sistema; nenhum manual diz o que acontece se ambos os mecanismos não forem suportados, presumivelmente ele aborta com um erro). O nome do arquivo é passado como argumento na linha de comando.


zshadicionalmente suporta =(list)como possível substituição para <(list). Com =(list)um arquivo temporário é usado em vez de arquivo /dev/fdou um FIFO. Pode ser usado como um substituto para <(list)se o programa precisar espreitar na saída.

De acordo com o manual da ZSH , também pode haver outros problemas de como <(list)funciona:

O =formulário é útil, pois a /dev/fdimplementação de pipe nomeado e o de <(...)têm desvantagens. No primeiro caso, alguns programas podem fechar automaticamente o descritor de arquivo em questão antes de examinar o arquivo na linha de comando, principalmente se isso for necessário por razões de segurança, como quando o programa está executando o setuid. No segundo caso, se o programa realmente não abrir o arquivo, o subshell que tenta ler ou gravar no canal irá (em uma implementação típica, sistemas operacionais diferentes podem ter um comportamento diferente) bloquear para sempre e precisar ser morto explicitamente . Nos dois casos, o shell realmente fornece as informações usando um pipe, para que os programas que esperam seguir (veja a página de manual lseek(2)) no arquivo não funcionem.

Adaephon
fonte
Isso me ajudou a descobrir por que o MacOS pfctl -f <(echo "pf rules")diria um descritor de arquivo incorreto. usando zsh e = (echo "pf rules") funciona.
johnnyB
9

Observe que esta é uma resposta básica, não zsh.

Há casos no bash em que você não pode usar pipes:

some_command | some_other_command

como os tubos introduzem subcascas para cada componente do encanamento, quando as subcascas saem, quaisquer efeitos colaterais dos quais você confiava desapareceriam. Por exemplo, este exemplo artificial:

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

exibirá uma linha em branco, porque a $countvariável não existe no shell atual.

Uma substituição de processo bash permite evitar esse enigma, permitindo que você leia a saída "some_command" como faria em um arquivo

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

(1) é um redirecionamento de entrada normal. (2) é o início da <()sintaxe de substituição do processo.

Glenn Jackman
fonte
2
= (cmdlist) no zsh tem quase o mesmo efeito que <(cmdlist) no bash, mas cria (e exclui quando pronto) um arquivo temporário com a saída de cmdlist para o redirecionamento. Isso é bom quando a busca é potencialmente feita no programa. <(cmdlist) também é conhecido pelo zsh.
Gombai Sándor 31/03