diff -r apenas para certos tipos de arquivo

12

Existe uma maneira de executar uma comparação recursiva de dois diretórios, mas apenas comparar (em seus respectivos locais) arquivos que correspondam a um predicado específico de nome de arquivo ou tipo de arquivo?

Por exemplo, eu gostaria de fazer algo como

diff -r dir-a dir-b -filenames *.java, ivy.xml, build.xml

... ou melhor ainda:

diff -r dir-a dir-b -filetype text

Claramente, não é obrigatório usá-lo, diffpois suponho que um encantamento finde -exec difftambém possa fazer o truque (simplesmente não sei como gerar os caminhos de arquivo complementares no último caso).

Marcus Junius Brutus
fonte
3
Há uma opção para excluir arquivos que correspondem a um padrão. Não vejo uma opção para incluir apenas arquivos que correspondam ao padrão.
Barmar
1
Todas as opções específicas para comparar diretórios podem ser encontradas em gnu.org/software/diffutils/manual/html_node/…
Barmar
1
veja este link e veja a resposta de Sérgio.
yehudahs
1
stackoverflow.com/q/10131908/2707864
sancho.s ReinstateMonicaCellio

Respostas:

1

Shellscript differ-r

Esse shellscript pode executar uma comparação recursiva de dois diretórios, mas apenas comparar (em seus respectivos locais) arquivos que correspondam a um nome de arquivo ou padrão de tipo de arquivo específico.

#!/bin/bash

greenvid="\0033[32m"
resetvid="\0033[0m"

if [ $# -ne 3 ]
then
 echo "Usage: compare files in two directories including subdirectories"
 echo "         $0 <source-dir> <target-dir> <pattern>"
 echo "Example: $0  subdir-1     subdir-2     \"*.txt\""
 exit
fi

cmd='for pathname do
        greenvid="\0033[32m"
        resetvid="\0033[0m"
        echo -e "${greenvid}diff \"$pathname\" \"${pathname/'\"$1\"'/'\"$2\"'}\"${resetvid}"
        diff "$pathname" "${pathname/'\"$1\"'/'\"$2\"'}"
    done'
#echo "$cmd"

find "$1" -type f -name "$3" -exec bash -c "$cmd" bash {} +

Demo

Arquivos:

$ find -type f
./1/ett.txt
./1/two.doc
./1/t r e.txt
./1/sub/only-one.doc
./1/sub/hello.doc
./1/sub/hejsan.doc
./differ-r2
./differ-r1
./differ-r
./2/ett.txt
./2/two.doc
./2/t r e.txt
./2/sub/hello.doc
./2/sub/hejsan.doc

Uso:

$ ./differ-r
Usage: compare files in two directories including subdirectories
         ./differ-r <source-dir> <target-dir> <pattern>
Example: ./differ-r  subdir-1     subdir-2     "*.txt"

Em execução differ-r:

As difflinhas de comando executadas são impressas com texto verde e a saída, quando não há correspondência, é impressa com texto padrão (branco em preto na captura de tela a seguir).

insira a descrição da imagem aqui

$ ./differ-r 1 2 "*.doc"
diff "1/two.doc" "2/two.doc"
diff "1/sub/only-one.doc" "2/sub/only-one.doc"
diff: 2/sub/only-one.doc: No such file or directory
diff "1/sub/hello.doc" "2/sub/hello.doc"
2d1
< world
diff "1/sub/hejsan.doc" "2/sub/hejsan.doc"

$ ./differ-r 1 2 "*.txt"
diff "1/ett.txt" "2/ett.txt"
2c2
< stabben
---
> farsan
diff "1/t r e.txt" "2/t r e.txt"
1c1
< t r e
---
> 3
$ 

$ ./differ-r 1 2 "*"
diff "1/ett.txt" "2/ett.txt"
2c2
< stabben
---
> farsan
diff "1/two.doc" "2/two.doc"
diff "1/t r e.txt" "2/t r e.txt"
1c1
< t r e
---
> 3
diff "1/sub/only-one.doc" "2/sub/only-one.doc"
diff: 2/sub/only-one.doc: No such file or directory
diff "1/sub/hello.doc" "2/sub/hello.doc"
2d1
< world
diff "1/sub/hejsan.doc" "2/sub/hejsan.doc"

$ ./differ-r 2 1 "*"
diff "2/ett.txt" "1/ett.txt"
2c2
< farsan
---
> stabben
diff "2/two.doc" "1/two.doc"
diff "2/t r e.txt" "1/t r e.txt"
1c1
< 3
---
> t r e
diff "2/sub/hello.doc" "1/sub/hello.doc"
1a2
> world
diff "2/sub/hejsan.doc" "1/sub/hejsan.doc"

rsync com filtro

Se você não precisar obter nenhuma saída que descreva a diferença, saiba apenas quais arquivos estão diferentes ou ausentes (para que rsynceles desejem copiá-los), você pode usar a seguinte linha de comando.

rsync --filter="+ <pattern>" --filter="+ */" --filter="- *"--filter="- */"  -avcn <source directory>/ <target directory>

Demo

$ rsync --filter="+ *.doc" --filter="+ */" --filter="- *"  -avcn 1/ 2
sending incremental file list
./
sub/
sub/hello.doc
sub/only-one.doc

sent 276 bytes  received 35 bytes  622.00 bytes/sec
total size is 40  speedup is 0.13 (DRY RUN)

sent 360 bytes  received 41 bytes  802.00 bytes/sec
total size is 61  speedup is 0.15 (DRY RUN)
olle@bionic64 /media/multimed-2/test/test0/temp $ rsync --filter="+ *.txt" --filter="+ */" --filter="- *" -avcn 1/ 2
sending incremental file list
./
ett.txt
t r e.txt
sub/

sent 184 bytes  received 29 bytes  426.00 bytes/sec
total size is 21  speedup is 0.10 (DRY RUN)

Se você deseja uma saída limpa, sem comentar linhas e sem diretórios, é possível que grepa saída seja assim,

$ pattern="*.doc"; rsync --filter="+ $pattern" --filter="+ */" --filter="- *"  -avcn 1/ 2 | grep "${pattern/\*/.\*}"
sub/hello.doc
sub/only-one.doc

Shellscript rsync-diff

Esse one-liner pode ser transformado no comando principal de um shellscript rsync-diff.

#!/bin/bash

LANG=C

if [ $# -ne 3 ]
then
 echo "Usage: compare files in two directories including subdirectories"
 echo "         $0 <source-dir> <target-dir> <pattern>"
 echo "Example: $0  subdir-1     subdir-2     \"*.txt\""
 exit
fi

pattern="$3"; rsync --filter="+ $pattern" --filter="+ */" --filter="- *" \
 -avcn "$1"/ "$2" | grep "${pattern//\*/.\*}" | grep -v \
  -e '/$' \
  -e '^sending incremental file list$' \
  -e '^sent.*received.*sec$' \
  -e '^total size is.*speedup.*(DRY RUN)$'
sudodus
fonte
0

Como você mencionou "Claramente, não é obrigatório usar o diff",

Isso deve fazer o trabalho para você mesclar facilmente configurável para que tipo de arquivos ignorar:

insira a descrição da imagem aqui

além disso, outra alternativa seria escrever um script simples que será transferido de uma lista branca para uma lista negra e, posteriormente, a lista negra será passada para o diff com a --excludeopção

JammingThebBits
fonte
tags atualizadas para adicionar 'linha de comando'
Marcus Junius Brutus
0

Com a substituição do comando de suporte ao shell, você pode usar o seguinte recurso (como já observado por @JammingThebBits):

diff -r dir-a dir-b --exclude-from=<( \
find dir-a dir-b -type f -not \( -name '*.xml'  -or -name '*.java' \) \
| sed 's:^.*/\([^/]*\)$:\1:' \
)

Funciona assim: findprocure os arquivos que não são do seu interesse, sedextraia o nome da base (a execução basenameé extremamente lenta se você tiver muitos arquivos) e os coloca em um arquivo temporário ; esse arquivo é passado para diffpedir para excluí-los da comparação (exclusão dupla = inclusão).

Se você não tiver substituição de comando, coloque a sedsaída em um arquivo e passe-a explicitamente diff.

No exemplo, procurei apenas arquivos XML e JAVA, altere-os conforme necessário, separando-os com OR.

Corrado
fonte