Linux / Comando diff: comparar arquivos, diretórios e salvar patches
O comando diff compara arquivos de texto linha por linha e pode comparar árvores de diretórios com -r. Use diff -u para ver alterações com contexto: no GNU diff, o estado de saída 0 significa ausência de diferenças, 1 indica diferenças encontradas e 2 indica um problema na comparação.
A prática abaixo prepara arquivos pequenos em uma pasta exclusiva, usando Bash e utilitários GNU. Execute os blocos na ordem e no mesmo terminal, sem trocar os nomes pelos de arquivos de produção. As opções seguem a documentação oficial do GNU diff; consulte também diff --version, diff --help e man diff na instalação de destino.
O que o diff compara?
A comparação descreve como transformar o conteúdo do primeiro arquivo no conteúdo do segundo. O diff lê as entradas e escreve um relatório; não aplica as mudanças aos arquivos. Sem opções que ignorem diferenças, duas entradas textuais iguais não produzem relatório. Ao usar filtros como -b ou -i, a ausência de relatório significa apenas que nenhuma diferença relevante para aquelas opções foi encontrada, e não necessariamente igualdade de bytes.
Sintaxe básica
diff [opções] -- ARQUIVO1 ARQUIVO2
diff [opções] -- DIRETORIO1 DIRETORIO2
Os termos entre colchetes e os nomes em maiúsculas são marcadores, não comandos para copiar literalmente. Use -- antes dos caminhos para encerrar as opções, e coloque caminhos entre aspas. A ordem importa: inverter os arquivos troca o sentido das inclusões e exclusões. Nos próximos blocos, os caminhos usam a variável diff_demo, criada uma única vez durante a preparação.
Uso básico do diff
Preparar os arquivos e comparar no formato tradicional
A pasta criada conterá duas configurações de três linhas: old.conf usa mode=dev e port=8080; new.conf usa mode=prod e port=9090. Ambas mantêm log=info. As árvores também terão uma alteração em sub/value.txt, um arquivo comum igual e added.txt presente apenas na árvore nova. Pare se a preparação falhar. A última comparação deve terminar com estado 1 porque há diferenças: no formato tradicional, 1,2c1,2 indica a troca das linhas 1 a 2, com < para o primeiro arquivo e > para o segundo.
diff_demo=$(mktemp -d -p "$PWD" 'diff-demo.XXXXXXXX') &&
mkdir -- "$diff_demo/old-tree" "$diff_demo/new-tree" \
"$diff_demo/old-tree/sub" "$diff_demo/new-tree/sub" &&
printf '%s\n' 'mode=dev' 'port=8080' 'log=info' > "$diff_demo/old.conf" &&
printf '%s\n' 'mode=prod' 'port=9090' 'log=info' > "$diff_demo/new.conf" &&
cp -- "$diff_demo/old.conf" "$diff_demo/old-tree/config.conf" &&
cp -- "$diff_demo/new.conf" "$diff_demo/new-tree/config.conf" &&
printf '%s\n' 'igual' > "$diff_demo/old-tree/sub/common.txt" &&
cp -- "$diff_demo/old-tree/sub/common.txt" "$diff_demo/new-tree/sub/common.txt" &&
printf '%s\n' 'antigo' > "$diff_demo/old-tree/sub/value.txt" &&
printf '%s\n' 'novo' > "$diff_demo/new-tree/sub/value.txt" &&
printf '%s\n' 'adicionado' > "$diff_demo/new-tree/added.txt" &&
printf '%s\n' 'old' > "$diff_demo/before.txt" &&
printf '%s\n' 'new' > "$diff_demo/after.txt" &&
printf '%s\n' 'alpha beta' > "$diff_demo/space-before.txt" &&
printf '%s\n' 'alpha beta' > "$diff_demo/space-after.txt" &&
diff -- "$diff_demo/old.conf" "$diff_demo/new.conf"
Ver mudanças com contexto no formato unificado
O formato unificado facilita revisar o que saiu, o que entrou e o que permaneceu. No formato documentado pelo GNU, os cabeçalhos --- e +++ identificam as entradas; @@ delimita um trecho, com posições e quantidades de linhas. Nesse trecho, espaço no início indica contexto, - indica remoção e + indica inclusão. Nesta prática, espere -mode=dev, +mode=prod, -port=8080 e +port=9090, além do contexto log=info. As datas dos cabeçalhos dependem da execução.
diff -u -- "$diff_demo/old.conf" "$diff_demo/new.conf"
Comparar as duas árvores de diretórios
A opção -r visita os subdiretórios correspondentes; -u mantém o relatório com contexto. Deve aparecer a alteração de config.conf e de sub/value.txt, além do aviso de que added.txt existe só em new-tree. O arquivo sub/common.txt, igual nos dois lados, normalmente fica silencioso. Sem -r, o diff não compara os arquivos dentro dos subdiretórios comuns. O relatório não altera as árvores nem copia arquivos entre elas.
diff -ru -- "$diff_demo/old-tree" "$diff_demo/new-tree"
Ler sinais de alteração e o estado de saída
Os arquivos before.txt e after.txt da preparação contêm, respectivamente, uma linha old e uma linha new. O bloco consulta esses arquivos sem recriá-los. A seguir aparece somente o conteúdo alterado, como recorte do relatório unificado completo: os cabeçalhos e a linha @@ não estão reproduzidos.
diff -u -- "$diff_demo/before.txt" "$diff_demo/after.txt"
Recorte das linhas alteradas:
-old
+new
Neste recorte, -old remove uma linha da primeira entrada, enquanto +new acrescenta uma linha para chegar à segunda. O estado 1 é esperado. Consulte $? imediatamente depois de diff se precisar inspecioná-lo manualmente: executar outro comando antes substitui esse valor. Em scripts, capture o resultado em um if, como no exemplo de gravação do patch adiante, e separe diferenças de problemas reais.
Principais opções
| Opção | Uso no GNU diff |
|---|---|
-u |
Mostrar trechos no formato unificado, com três linhas de contexto por padrão; -U N escolhe outra quantidade. |
-r, --recursive |
Comparar arquivos também nos subdiretórios correspondentes. |
-q, --brief |
Relatar se os arquivos diferem, sem listar cada linha alterada. |
-s, --report-identical-files |
Relatar explicitamente pares considerados iguais. |
-w, --ignore-all-space |
Ignorar diferenças de espaços dentro das linhas, inclusive presença contra ausência; não eliminar mudanças de quebra de linha. |
-b, --ignore-space-change |
Ignorar espaços no fim da linha e diferenças na quantidade de espaços entre trechos, mantendo a distinção entre separador presente e ausente. |
-i, --ignore-case |
Ignorar diferenças de maiúsculas e minúsculas no conteúdo. |
--color=auto |
Usar cores quando a saída for um terminal, nas versões GNU que oferecem a opção. |
Exemplos práticos
Salvar um patch sem sobrescrever um arquivo existente
Um patch é um arquivo com a descrição das alterações, para revisão ou aplicação posterior por uma ferramenta apropriada. O subshell entra na pasta da prática e abre config.patch com noclobber: se esse nome já existir, o bloco para antes da comparação. A abertura é verificada separadamente, para não confundir uma recusa de gravação com o estado 1 de diff. Os caminhos relativos old.conf e new.conf ficam nos cabeçalhos. Leia o arquivo salvo; a mensagem de criação não significa que as alterações foram aplicadas. Se ocorrer estado 2, considere o relatório incompleto e examine a mensagem de erro; não o use como patch válido.
(
cd -- "$diff_demo" || exit
set -C
exec 3>config.patch || exit
if diff -u -- old.conf new.conf >&3; then
result=0
else
result=$?
fi
exec 3>&-
case $result in
0) printf 'Sem diferencas; patch vazio.\n' ;;
1) printf 'Patch criado em %s/config.patch\n' "$diff_demo" ;;
*) printf 'Falha na comparacao; confira stderr e o arquivo.\n' >&2
exit "$result" ;;
esac
)
Resumir diferenças entre diretórios
A combinação -qr deve relatar config.conf e sub/value.txt como diferentes e added.txt como presente apenas na árvore nova, sem o conteúdo das linhas. O estado global é 1. Para incluir os pares iguais no relatório, acrescente -s; isso fará sub/common.txt aparecer. A comparação de diretórios do GNU considera os caminhos correspondentes: ela não identifica automaticamente um arquivo renomeado como a mesma entidade.
diff -qr -- "$diff_demo/old-tree" "$diff_demo/new-tree"
Distinguir quantidade de espaços e ausência de separador
A preparação cria space-before.txt com alpha beta e space-after.txt com alpha beta. Sem -b, há diferença; com o bloco abaixo, espere silêncio e estado 0, pois apenas a quantidade de espaços muda. Já comparar alpha beta com alphabeta ainda produz diferença com -b, enquanto -w ignora esse separador. Conforme a documentação sobre espaços, -w não ignora mudanças que envolvem quebras de linha. Use esses filtros apenas quando os espaços forem irrelevantes para o formato.
diff -u -b -- "$diff_demo/space-before.txt" "$diff_demo/space-after.txt"
Comparar diff e comandos relacionados
| Comando | Função principal | Quando usar |
|---|---|---|
diff |
Comparar texto por linhas e caminhos correspondentes | Revisar configurações, fontes e árvores de diretórios |
cmp |
Comparar bytes e indicar a primeira diferença | Conferir igualdade exata e posição de uma diferença |
git diff |
Comparar mudanças no contexto do Git | Revisar alterações da árvore de trabalho e de commits |
comm |
Separar linhas comuns e exclusivas de entradas ordenadas | Comparar listas previamente ordenadas sob o mesmo critério |
Cuidados importantes
Distinguir diferenças de erro e de saída incompleta
O GNU diff usa 0 para nenhuma diferença, 1 para diferenças e 2 para problemas, como entrada inexistente. Silêncio em stdout não prova êxito: verifique também stderr e o estado final. Não use apenas diff ... || echo erro, pois isso mistura estados 1 e 2. Em um script com set -e, uma comparação simples que retorna 1 pode interromper o fluxo; uma condição if permite tratar esse resultado de forma deliberada. Confira ainda se os arquivos podem mudar durante a leitura: o relatório não é uma captura atômica de arquivos ativos.
Não confundir igualdade de conteúdo com cópia completa
Para igualdade byte a byte, prefira cmp. O diff pode apenas avisar que arquivos binários diferem; forçar -a transforma a análise em linhas e pode gerar uma saída ilegível. A comparação de conteúdo não certifica permissões, proprietário ou todos os metadados. Em árvores, links simbólicos podem levar a outros destinos; confira --no-dereference quando a intenção for comparar o link em si. Não trate uma árvore sem diferenças de conteúdo como prova de que um backup preservou todos os atributos.
Conferir espaços, codificação e finais de linha
Filtros de espaço ou de maiúsculas podem ocultar diferenças importantes, como indentação de código. Arquivos com codificações diferentes também podem representar o mesmo texto com bytes distintos; diff não faz conversão automática para uma codificação comum. Finais de linha LF e CRLF e uma última linha sem quebra merecem exame próprio. Em GNU diff, --strip-trailing-cr desconsidera CR antes de LF quando essa normalização for apropriada; o tratamento de texto também depende da plataforma. Recompare sem filtros antes de concluir que uma alteração é apenas de apresentação.
Perguntas frequentes
O que significa o diff não mostrar nada?
Confira o estado: 0 significa que não houve diferenças sob as opções escolhidas. Isso não garante igualdade de bytes se houver filtros ativos, e stdout vazio sozinho não descarta um erro indicado em stderr.
Como ler uma comparação com contexto?
Use -u. Os cabeçalhos identificam as entradas, @@ delimita o trecho, e espaço, - e + indicam contexto, remoção e inclusão. A interpretação segue a ordem do primeiro arquivo para o segundo.
O diff compara subdiretórios?
Sim, com -r. Use -qr para um resumo e confira também mensagens sobre caminhos existentes em apenas um lado. Permissões, links e arquivos que mudam durante a leitura limitam a interpretação.
O estado 1 significa que a comparação falhou?
No GNU diff, não: 1 indica diferenças encontradas. O estado 2 indica um problema. Em automações, capture o resultado e trate os dois casos separadamente, inclusive ao salvar o relatório.




