Tutorial de Linux / Comparar arquivos com diff e cmp

Use diff para entender quais linhas de texto mudaram e cmp para verificar se dois arquivos têm o mesmo conteúdo byte a byte. Nas comparações comuns do GNU Diffutils, o estado de saída 1 indica uma diferença encontrada; 2 indica um problema na comparação, como um caminho inexistente. Nesta prática, crie dois arquivos pequenos e leia tanto a saída quanto o estado retornado.

Para preparar e inspecionar o texto, consulte a aula de leitura de arquivos com cat, less, more, head e tail. Ao combinar comparações com condições, lembre que encontrar uma diferença é diferente de não conseguir comparar; a aula de estado de saída e execução condicional, em inglês explica o fluxo no shell.

Preparar os dois arquivos

A prática usa Bash e GNU Diffutils, em um diretório no qual o usuário comum possa gravar. Se ~/linux-course-13-practice já existir, escolha outro nome e substitua-o nas duas primeiras linhas. Execute uma linha por vez e pare se mkdir ou cd falhar: as redireções > usadas depois podem truncar arquivos existentes.

mkdir ~/linux-course-13-practice
cd ~/linux-course-13-practice
pwd

Confirme que pwd mostra o diretório novo e que old.txt e new.txt ainda não existem. O sucesso de pwd, sozinho, não garante que cd funcionou. Crie os arquivos abaixo: apenas a segunda linha difere, com beta no primeiro e BETA no segundo. Cada \n produz uma nova linha.

printf 'alpha\nbeta\ngamma\n' > old.txt
printf 'alpha\nBETA\ngamma\n' > new.txt

Ler as alterações de linhas com diff

diff compara o texto por linhas. A opção -u seleciona o formato unificado, que mostra a alteração junto de linhas vizinhas que não mudaram. No GNU diff, as duas opções --label definem cabeçalhos estáveis para o primeiro e o segundo arquivo, no lugar dos nomes e horários de modificação normalmente exibidos.

diff -u --label old --label new old.txt new.txt

Com os arquivos desta prática, a saída é:

--- old
+++ new
@@ -1,3 +1,3 @@
 alpha
-beta
+BETA
 gamma

Os cabeçalhos --- old e +++ new identificam o primeiro e o segundo argumento. A linha @@ -1,3 +1,3 @@ indica um trecho que começa na linha 1 e contém três linhas em cada arquivo. Uma linha de conteúdo com - vem do primeiro arquivo; com +, vem do segundo. As linhas com um espaço inicial são o contexto comum aos dois.

Leia o trecho como a substituição de beta por BETA, mantendo alpha e gamma. A ordem dos argumentos define a direção da comparação; diff não descobre qual arquivo você considera o original. Trocar a ordem inverte os papéis de remoção e adição. A comparação apenas relata as diferenças e não aplica alterações aos arquivos.

Consultar a primeira diferença de bytes com cmp

cmp compara os bytes. Sem opções que alterem sua saída, ele normalmente informa a posição da primeira diferença. Neste exemplo, alpha ocupa cinco bytes ASCII, seguidos por um byte de nova linha; portanto, a primeira diferença está no byte 7, linha 2, entre b e B.

cmp old.txt new.txt

A mensagem em uma localidade de língua inglesa pode aparecer como old.txt new.txt differ: char 7, line 2 ou usar a palavra byte. Os nomes e a redação variam com a versão e a localidade. A posição começa em 1 e refere-se a bytes, mesmo quando a mensagem usa char; com texto multibyte, ela não representa necessariamente a posição de uma letra visível.

A saída padrão de cmp não é uma lista de todas as alterações posteriores. Há opções como -l para listar posições diferentes. Se um arquivo terminar antes do outro, a diferença também pode ser informada como EOF, fim de arquivo, em vez de uma troca entre dois bytes. Esse resultado continua indicando diferença, não necessariamente uma falha de leitura.

Para verificar a igualdade sem texto de resultado, use -s e examine imediatamente o estado de saída. Os arquivos desta prática são diferentes, então o bloco imprime 1:

cmp -s old.txt new.txt
echo $?

$? é o estado do comando concluído mais recentemente. Não coloque outro comando entre cmp e echo, pois você passaria a consultar esse outro comando. Depois de echo, o estado já é o de echo. Com -s, a ausência de texto não prova que os arquivos são iguais: é preciso distinguir 0, 1 e 2.

Distinguir diferença de erro

Estado Significado comum em diff e cmp O que conferir
0 Nenhuma diferença segundo os critérios usados Se os arquivos e as opções eram os pretendidos
1 Diferença encontrada As linhas alteradas com diff ou a posição indicada por cmp
2 Problema ao comparar, como arquivo inacessível Caminhos, permissões e mensagens de erro

Uma comparação de um arquivo consigo mesmo normalmente não produz saída e retorna 0. Os dois arquivos diferentes preparados aqui retornam 1; um caminho inexistente pode provocar 2. Ao usar esses resultados em um script, trate as situações separadamente, em vez de considerar todo estado diferente de zero como o mesmo erro.

Confira também as opções. Por exemplo, diff -i old.txt new.txt ignora a diferença entre maiúsculas e minúsculas e não encontra alteração neste par. Isso não torna os bytes iguais: cmp sem opções de ignorar dados continua detectando a diferença. Esses comandos comparam o conteúdo, e não a igualdade das permissões ou dos horários dos arquivos.

Para ler uma mudança de texto, prefira diff -u; para a igualdade exata dos bytes, use cmp com os arquivos completos. Diferenças de finais de linha, como LF e CRLF, ou de codificação podem existir mesmo quando o texto parece igual no editor. Ao detectar arquivos binários, diff normalmente informa apenas que são diferentes; cmp ajuda a começar pela comparação dos bytes.

Se precisar comparar árvores de diretórios, o GNU diff -r percorre recursivamente os pares de arquivos correspondentes. Confira os dois caminhos antes de ampliar o escopo. A saída pode ser maior, e arquivos sem leitura permitida precisam ser distinguidos de diferenças de conteúdo. Para mais opções, consulte os guias de diff e cmp.

Documentação oficial

O manual oficial do GNU Diffutils descreve o formato unificado, os cabeçalhos com --label, a saída e as opções de cmp, as comparações recursivas e os estados de saída de ambos os comandos.