Difere a saída de dois programas sem arquivos temporários

Respostas:

211

Use <(command)para passar a saída de um comando para outro programa como se fosse um nome de arquivo. O Bash canaliza a saída do programa para um canal e passa um nome de arquivo como /dev/fd/63para o comando externo.

diff <(./a) <(./b)

Da mesma forma, você pode usar >(command)se quiser canalizar algo em um comando.

Isso é chamado de "Substituição de Processo" na página do manual do Bash.

John Kugelman
fonte
1
Uma desvantagem é que, se ./a ou ./b falhar, o chamador não descobrirá isso.
Alexander Pogrebnyak
5
O OP marcou a questão como bash, mas, para constar, isso não funciona em nenhum outro shell. É uma extensão do bash para o padrão utilitário Posix.
DigitalRoss
5
Eu tentei esta solução com um programa Java e tenho esse erro: -bash: syntax error near unexpected token ('. Eu tentei novamente sem parênteses e consegui -bash: java: No such file or directory. Não funciona se o comando tiver parâmetros?
Styfle
1
@DigitalRoss - A solução pode ser estendida para outros shells usando um alias. Em tcsh, o seguinte feiúra funciona: alias diffcmd bash -c \'diff \<\(sh -c \!:1\) \<\( sh -c \!:2 \)\'. (Então, por exemplo: diffcmd "ls" "ls -a").
Paul Lynch
Para quem está fora do Google, isso também funciona no zsh. (Além disso, se você precisa para entrada de alimentação em algo que as chamadas fseek, ofertas zsh =(./a)que podem ser usadas de forma idêntica para <(./a), mas usa um arquivo temporário sob o capô, que zsh vai apagar para você.)
ssokolow
26

Adicionando a ambas as respostas, se você quiser ver uma comparação lado a lado, use vimdiff:

vimdiff <(./a) <(./b)

Algo assim:

insira a descrição da imagem aqui

pé quebrado
fonte
vimdiffcria visualizações de comparação de diferenças bonitas, inteligentes e interativas. Parece vir com o vimpacote na maioria dos sistemas.
Tim Visée
vimdifftambém mostra não apenas a linha que difere, mas também o fragmento de texto específico que difere.
Anton Tarasenko 27/11
20

Uma opção seria usar pipes nomeados (FIFOs) :

mkfifo a_fifo b_fifo
./a > a_fifo &
./b > b_fifo &
diff a_fifo b_fifo

... mas a solução de John Kugelman é muito mais limpa.

Martin Clayton
fonte
É melhor remover os pipes nomeados depois de usá-los, com rm a_fifo b_fifo.
Franklin Yu
15

Para qualquer pessoa curiosa, é assim que você executa a substituição do processo usando o shell Fish :

Bash:

diff <(./a) <(./b)

Peixe:

diff (./a | psub) (./b | psub)

Infelizmente, a implementação em peixes é atualmente deficiente ; O peixe travará ou usará um arquivo temporário no disco. Você também não pode usar o psub para saída do seu comando.

James McMahon
fonte
Atualmente, isso não funciona corretamente em peixes. Se a saída dos programas for maior que um BUFSIZ, o comando será interrompido. (ou peixe vai realmente usar apenas um arquivo temporário no disco)
Evan Benn
7

Adicionando um pouco mais às já boas respostas (me ajudaram!):

O comando dockerenvia sua ajuda para STD_ERR(ou seja, descritor de arquivo 2)

Eu queria ver se docker attache docker attach --helpdeu a mesma saída

$ docker attach

$ docker attach --help

Depois de digitar esses dois comandos, fiz o seguinte:

$ diff <(!-2 2>&1) <(!! 2>&1)

!! é o mesmo que! -1, o que significa executar o comando 1 antes deste - o último comando

! -2 significa executar o comando dois antes deste

2> & 1 significa enviar a saída file_descriptor 2 (STD_ERR) para o mesmo local que a saída file_descriptor 1 (STD_OUT)

Espero que isso tenha sido útil.

darrenthatcher
fonte
0

Para zsh, usar =(command)automaticamente cria um arquivo temporário e substitui =(command)o caminho do próprio arquivo. Com a Substituição de Processo normal, $(command)é substituída pela saída do comando.

Esse recurso zsh é muito útil e pode ser usado assim para comparar a saída de dois comandos usando uma ferramenta diff, por exemplo, Beyond Compare:

bcomp  =(ulimit -Sa | sort) =(ulimit -Ha | sort)

Para Além da comparação, observe que você deve usar bcompo acima (em vez de bcompare), pois bcompinicia a comparação e aguarda a conclusão. Se você usar bcompare, isso inicia a comparação e sai imediatamente, devido ao qual os arquivos temporários criados para armazenar a saída dos comandos desaparecem.

Leia mais aqui: http://zsh.sourceforge.net/Intro/intro_7.html

Observe também o seguinte:

Observe que o shell cria um arquivo temporário e o exclui quando o comando é concluído.

e o seguinte, que é a diferença entre $(...)e =(...):

Se você ler a página de manual do zsh, poderá notar que <(...) é outra forma de substituição de processo semelhante a = (...). Há uma diferença importante entre os dois. No caso <(...), o shell cria um pipe nomeado (FIFO) em vez de um arquivo. Isso é melhor, pois não preenche o sistema de arquivos; mas não funciona em todos os casos. De fato, se substituíssemos = (...) por <(...) nos exemplos acima, todos eles teriam parado de funcionar, exceto fgrep -f <(...). Você não pode editar um canal ou abri-lo como uma pasta de correio; O fgrep, no entanto, não tem problemas em ler uma lista de palavras de um pipe. Você pode se perguntar por que a barra diff <(foo) não funciona, pois foo | obras de difusão; isso ocorre porque o diff cria um arquivo temporário se perceber que um de seus argumentos é - e copia sua entrada padrão no arquivo temporário.

Ashutosh Jindal
fonte