De acordo com as Leis 12.965/2014 e 13.709/2018, que regulam o uso da Internet e o tratamento de dados pessoais no Brasil, ao me inscrever na newsletter do portal DICAS-L, autorizo o envio de notificações por e-mail ou outros meios e declaro estar ciente e concordar com seus Termos de Uso e Política de Privacidade.

Uma análise mais aprofundada acerca do "diff -Naur"

Colaboração: Jamenson Ferreira Espindula de Almeida Melo

Data de Publicação: 3 de março de 2025

O programa diff (que é originário do pacote GNU diffutils) é usado para identificar e mostrar as diferenças existentes entre dois arquivos. Ele é muitíssimo usado no mundo do software livre em geral. No texto a seguir, buscou-se analisar mais detalhadamente alguns atributos do binário executável (na seção "contextualização do software"), bem como quatro parâmetros específicos muito usados (na seção "objeto").

1 - Contextualização do software

$ which diff
/usr/bin/diff
$ ls -l /usr/bin/diff
-rwxr-xr-x 1 root root 208320 jan  1  2021 /usr/bin/diff

$ file /usr/bin/diff

/usr/bin/diff: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV),\
dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2,\
BuildID[sha1]=25fb8c91853f91edb1a78a6503407f212467f492, for GNU/Linux 3.2.0,\
stripped


$ ldd /usr/bin/diff

    linux-vdso.so.1 (0x00007ffcc3b0c000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fef9ac17000)
    /lib64/ld-linux-x86-64.so.2 (0x00007fef9ae3b000)


$ /usr/bin/diff --version

diff (GNU diffutils) 3.7
Copyright (C) 2018 Free Software Foundation, Inc.
Licença GPLv3+: GNU GPL versão 3 ou posterior

Este é um software livre: você é livre para alterá-lo e redistribuí-lo.
NÃO HÁ QUALQUER GARANTIA, na máxima extensão permitida em lei.

Escrito por Paul Eggert, Mike Haertel, David Hayes
Richard Stallman e Len Tower.


$ cat /usr/lib/os-release

PRETTY_NAME="Debian GNU/Linux 11 (bullseye)"
NAME="Debian GNU/Linux"
VERSION_ID="11"
VERSION="11 (bullseye)"
VERSION_CODENAME=bullseye
ID=debian
HOME_URL="https://www.debian.org/"
SUPPORT_URL="https://www.debian.org/support"
BUG_REPORT_URL="https://bugs.debian.org/"

2 - Objeto

Este artigo tem por objetivo analisar mais detalhadamente o famosíssimo comando diff -Naur, sem pretender esgotar o assunto.

3 - Detalhamento dos parâmetros (texto livremente traduzido a partir do manual Info do software)

Parâmetro Explicação
-a
--text
Tratar todos os arquivos como texto e compará-los linha-a-linha, mesmo se eles não pareçam ser texto.
-N
--new-file
Se um arquivo estiver ausente, trata-o como presente, mas vazio.
-r
--recursive
Ao comparar diretórios, compare recursivamente quaisquer subdiretórios encontrados.
-u Use o formato unificado de saída, mostrando três linhas do contexto.

4 - Aprofundando

$ diff antes.txt depois.txt

$ echo "${?}"

0


$ md5sum antes.txt depois.txt

3b1442125e3bbb96a35c6010ea050b41  antes.txt
3b1442125e3bbb96a35c6010ea050b41  depois.txt


$ sha256sum antes.txt depois.txt

6f364e089ad031ee383cb71697066098dc58134745feac02d7ce4a6eee3513da  antes.txt
6f364e089ad031ee383cb71697066098dc58134745feac02d7ce4a6eee3513da  depois.txt

Observação: a sequência acima demonstra que, sendo igual o conteúdo dos arquivos comparados, o binário diff foi projetado para retornar zero como código da situação de saída.

Formato da Saída Gerada: "Normal":

$ diff --normal antes.txt depois.txt

1c1
< Primeira Linha
—-
> Primeira linha

Texto livremente traduzido a partir do manual Info do "diff":

FcT (F = From = Origem) (T = To = Destino)

Substitui as linhas no intervalo F do primeiro arquivo pelas linhas no intervalo T do segundo arquivo. Isso é como um adicionar e deletar combinados, porém mais compacto. Por exemplo, '5,7c8,10' significa mudar linhas 5-7 do arquivo 1 para ler como linhas 8-10 do arquivo 2; ou, se mudar arquivo 2 para arquivo 1, muda as linhas 8 a 10 do arquivo 2 para ler as linhas 5 a 7 do arquivo 1.

<: Os arquivos diferem e somente o primeiro arquivo contém a linha.

>: Os arquivos diferem e somente o segundo arquivo contém a linha.

Observações

  1. a única diferença entre o conteúdo dos dois arquivos comparados é somente a letra L (maiúscula) no primeiro arquivo (antes.txt); no segundo arquivo, o l (minúsculo).

  2. nesse formato de saída, coincidentemente, existe ambiguidade: impossível saber qual arquivo muda qual, se Origem muda Destino ou se Destino muda Origem.

  3. o programa patch informou erro ao tentar aplicar esse remendo, com esse formato.

Formato da Saída Gerada: "Contexto":

Com zero linhas de contexto:

$ diff --context=0 antes.txt depois.txt

*** antes.txt   2024-08-16 13:43:57.347008123 -0300
—- depois.txt  2024-08-16 13:49:11.971020374 -0300
***************
*** 1 ****
! Primeira Linha
—- 1 ----
! Primeira linha

Com três linhas de contexto (que é a quantidade padrão):

$ diff --context antes.txt depois.txt

*** antes.txt   2024-08-16 13:43:57.347008123 -0300
—- depois.txt  2024-08-16 13:49:11.971020374 -0300
***************
*** 1,2 ****
! Primeira Linha
  Segunda Linha
—- 1,2 ----
! Primeira linha
  Segunda Linha

Observação: o diff informa que as linhas diferentes são aquelas iniciadas pelo caractere exclamação !.

Formato da Saída Gerada: "Unificado" (com zero linhas de contexto):

$ diff --unified=0 antes.txt depois.txt

—- antes.txt   2024-08-16 13:43:57.347008123 -0300
+++ depois.txt  2024-08-16 13:49:11.971020374 -0300

-1 +1
-Primeira Linha +Primeira linha

Observação: o diff gera as seguintes informações:

  1. O nome do arquivo a ser substituído (---);
  2. O nome do arquivo que o substituirá (+++);
  3. O número da linha diferente, tanto no arquivo a ser substituído, quanto no arquivo que o substituirá (na hipótese, a linha um em ambos);
  4. O conteúdo das linhas diferentes, tanto a linha a ser substituída (iniciada pelo caractere menos -), quanto a linha que a substituirá (iniciada pelo caractere +);

E efetivamente, quando se aplica o remendo gerado com o comando acima, o resultado é este:

$ patch --input=diff.patch
patching file antes.txt

$ echo "${?}"
0

Situação interessante ocorre quando se tenta aplicar o mesmo remendo uma segunda vez sobre o mesmo arquivo já remendado:

$ patch --input=diff.patch

patching file antes.txt
Reversed (or previously applied) patch detected!  Assume -R? [n]
Apply anyway? [n]
Skipping patch.
1 out of 1 hunk ignored -- saving rejects to file antes.txt.rej


$ cat antes.txt.rej

—- antes.txt   2024-08-16 13:43:57.347008123 -0300
+++ depois.txt  2024-08-16 13:49:11.971020374 -0300

-1 +1
-Primeira Linha +Primeira linha
$ md5sum diff.patch antes.txt.rej fed0dda8642b843162f6267bb809a2ee diff.patch fed0dda8642b843162f6267bb809a2ee antes.txt.rej

Observação: o diff, nessa situação, gera um arquivo (com extensão .rej), cujo conteúdo é exatamente o do remendo tentado ser aplicado duas vezes.

Também é possível mudar-se (personalizar-se) o nome dos arquivos cujos conteúdos serão comparados:

$ diff --unified=0 --label=original --label=mudado antes.txt depois.txt

—- original
+++ mudado

-2 +2
-Segunda Linha +Segunda linha
$ echo "${?}" 1

Observações

  1. o nome do primeiro arquivo (antes.txt) recebeu o nome original no conteúdo do remendo.

  2. o nome do segundo arquivo (depois.txt) recebeu o nome mudado no conteúdo do remendo.

Porém, quando se tenta aplicar o remendo gerado pelo método acima com o habitual comando patch, um erro ocorre:

$ patch --input=diff.patch

can't find file to patch at input line 3
Perhaps you should have used the -p or --strip option?
The text leading up to this was:
—------------------------
|--- original
|+++ mudado
—------------------------
File to patch: antes.txt
patching file antes.txt

Como a opção --label mudou o nome do arquivo, mas somente dentro do conteúdo do remendo, nessa situação, é obrigatório informar o nome do arquivo a ser remendado.

Jamenson Ferreira Espindula de Almeida Melo
Jaboatão dos Guararapes, Pernambuco, Brasil
Usuário GNU/Linux nº 166197; LFS ID 24492
Key fingerprint: 234D 1914 4224 7C53 BD13 6855 2AE0 25C0 08A8 6180

Adicionar comentário

* Campos obrigatórios
5000
Powered by Commentics

Comentários

Nenhum comentário ainda. Seja o primeiro!


Veja a relação completa dos artigos de Jamenson Ferreira Espindula de Almeida Melo