linux diff tools: cria lista de arquivos modificados

13

Como faço para criar uma lista de arquivos modificados programaticamente usando ferramentas de linha de comando do Linux? Eu não estou interessado na diferença em qualquer arquivo particular (delta, patch). Eu só quero ter uma lista de arquivos novos ou modificados em comparação com o lançamento do produto anterior. Para poder publicar uma nova atualização de produto.

update: diff -qr não produz resultados muito convenientes. A saída de diff -qr também precisa ser processada. Existe alguma maneira melhor?

    
por Alpha Sisyphus 03.10.2011 / 15:15

12 respostas

8

Eu tenho uma abordagem simples para isso: Use o modo de visualização rsync:

rsync -aHSvn --delete old_dir/ new-dir/

Os arquivos mostrados como "a serem excluídos" por esse comando serão os "novos" arquivos. Os outros que serão transferidos mudaram de alguma forma. Veja a página rsync-man para mais detalhes.

    
por 03.10.2011 / 23:36
12

Você pode usar o diff toool: veja as opções -q e -r

-q  --brief
Output only whether files differ.

-r  --recursive
Recursively compare any subdirectories found.

Exemplo:

diff -qr dir1 dir2
    
por 03.10.2011 / 15:18
3

O pacote diffutils inclui uma ferramenta lsdiff . Apenas passe a saída de diff -u para lsdiff:

diff -u --other-diff-options path1 path2 | lsdiff
    
por 03.10.2011 / 21:10
1

Gostaria apenas de tocar em um arquivo no momento de cada atualização e, em seguida, você pode encontrar arquivos que foram modificados desde então com find /tree/location -newer /last/update/file -print

    
por 03.10.2011 / 15:18
1

Para pegar apenas o nome dos arquivos que eles mudaram, eu uso este comando:

diff -r dirt1 dir2 --brief | sed 's/^Only in \([^:]*\): /\//' | sed 's/^Files \(.*\) and .* differ//'

Se for necessário excluir alguns arquivos como arquivos de objeto ou arquivos de biblioteca, você poderá usar:

diff -r dirt1 dir2 --brief --exclude "*.o" --exclude "*.a" | sed 's/^Only in \([^:]*\): /\//' | sed 's/^Files \(.*\) and .* differ//'
    
por 21.08.2013 / 06:17
1

Para criar uma lista de arquivos novos ou modificados programaticamente, a melhor solução que posso usar é usar rsync , classificar e uniq :

(rsync -rcn --out-format="%n" old/ new/ && rsync -rcn --out-format="%n" new/ old/) | sort | uniq

Deixe-me explicar com este exemplo: queremos comparar duas versões dokuwiki para ver quais arquivos foram alterados e quais foram criados recentemente.

Nós buscamos os tars com wget e os extraímos nos diretórios old/ e new/ :

wget http://download.dokuwiki.org/src/dokuwiki/dokuwiki-2014-09-29d.tgz
wget http://download.dokuwiki.org/src/dokuwiki/dokuwiki-2014-09-29.tgz
mkdir old && tar xzf dokuwiki-2014-09-29.tgz -C old --strip-components=1
mkdir new && tar xzf dokuwiki-2014-09-29d.tgz -C new --strip-components=1

A execução do rsync de uma maneira pode perder arquivos recém-criados, como mostra a comparação do rsync e diff aqui:

rsync -rcn --out-format="%n" old/ new/

produz o seguinte resultado:

VERSION
doku.php
conf/mime.conf
inc/auth.php
inc/lang/no/lang.php
lib/plugins/acl/remote.php
lib/plugins/authplain/auth.php
lib/plugins/usermanager/admin.php

Rodar o rsync somente em uma direção erra os arquivos recém-criados e o inverso perderia arquivos deletados, compare a saída do diff:

diff -qr old/ new/

produz o seguinte resultado:

Files old/VERSION and new/VERSION differ
Files old/conf/mime.conf and new/conf/mime.conf differ
Only in new/data/pages: playground
Files old/doku.php and new/doku.php differ
Files old/inc/auth.php and new/inc/auth.php differ
Files old/inc/lang/no/lang.php and new/inc/lang/no/lang.php differ
Files old/lib/plugins/acl/remote.php and new/lib/plugins/acl/remote.php differ
Files old/lib/plugins/authplain/auth.php and new/lib/plugins/authplain/auth.php differ
Files old/lib/plugins/usermanager/admin.php and new/lib/plugins/usermanager/admin.php differ

Executar rsync nos dois sentidos e classificar a saída para remover duplicatas revela que o diretório data/pages/playground/ e o arquivo data/pages/playground/playground.txt foram perdidos inicialmente:

(rsync -rcn --out-format="%n" old/ new/ && rsync -rcn --out-format="%n" new/ old/) | sort | uniq

produz o seguinte resultado:

VERSION
conf/mime.conf
data/pages/playground/
data/pages/playground/playground.txt
doku.php
inc/auth.php
inc/lang/no/lang.php
lib/plugins/acl/remote.php
lib/plugins/authplain/auth.php
lib/plugins/usermanager/admin.php

rsync é executado com esses argumentos:

  • -r para "recursionar em diretórios",
  • -c para comparar também arquivos de tamanho idêntico e apenas "pular com base na soma de verificação, não no tamanho e tamanho da modalidade",
  • -n para "executar uma avaliação sem alterações" e
  • --out-format="%n" para "enviar atualizações usando o FORMATO especificado", que é "% n" aqui apenas para o nome do arquivo

A saída (lista de arquivos) de rsync em ambas as direções é combinada e classificada usando sort , e essa lista classificada é condensada removendo todos os duplicados com uniq

    
por 24.03.2015 / 22:36
0

Você deve obter o resultado desejado usando:

diff -r --brief dir1/ dir2/
    
por 03.10.2011 / 15:22
0

Isso pode ajudar:

compare_dirs()
{
    # Shows which files and directories exist in one directory but not both
    if [ $# -ne 2 ]
    then
        echo "Usage: compare_dirs dir1 dir2" >&2
        return 2
    fi
    for path
    do
        if [ ! -d "$path" ]
        then
            echo "Not a directory: $path" >&2
            return 1
        fi
    done
    comm -3 \
        <(cd -- "$1" && find . -printf '%P
compare_dirs()
{
    # Shows which files and directories exist in one directory but not both
    if [ $# -ne 2 ]
    then
        echo "Usage: compare_dirs dir1 dir2" >&2
        return 2
    fi
    for path
    do
        if [ ! -d "$path" ]
        then
            echo "Not a directory: $path" >&2
            return 1
        fi
    done
    comm -3 \
        <(cd -- "$1" && find . -printf '%P%pre%' | sort -z | quote_shell) \
        <(cd -- "$2" && find . -printf '%P%pre%' | sort -z | quote_shell)
}
' | sort -z | quote_shell) \ <(cd -- "$2" && find . -printf '%P%pre%' | sort -z | quote_shell) }
    
por 03.10.2011 / 15:47
0

Normalmente, você coloca os arquivos em algum tipo de sistema de controle de versão, como SubVersion ou git, já que eles podem fazer isso para você imediatamente.

Mas você pode fazer um script rápido com um loop for no dir1 e comparar todos os arquivos com o do dir2. O loop for pode ver o código de saída do diff para saber se os arquivos eram diferentes.

Talvez algo assim:

for f in '(cd dir1 ; find .)'
do 
  diff $f ../dir2/$f
  if [ "$?" == "0" ]
  then 
    echo same
  else 
    echo diff: $f
  fi
done

Nota: O script não é testado, por isso o exemplo acima é "basse pseudocode inspirado" ...

Vamos dar outra chance, mas com git

Crie alguns arquivos de exemplo para jogar com

mkdir -p dir1/test1/test11
mkdir -p dir1/test1/test12
mkdir -p dir1/test1/test13
echo "Test1" >> dir1/test1/test11/t1.txt
echo "Test2" >> dir1/test1/test12/t2.txt
echo "Test3" >> dir1/test1/test13/t3.txt

#And a dir to work in
mkdir gitdir

Em seguida, digite o diretório e import dir1

cd gitdir/
git init .
cp -r ../dir1/* .
git add .
git commit -m 'dir1'

Saia e modifique o dir1 (assim ele se torna seu dir2)

cd ..
echo "Test2" > dir1/test1/test11/t1.txt

Em seguida, vá para o diretório git e importe o novo diretório

cd gitdir/
cp -r ../dir1/* .

Agora pergunte ao git o que mudou (com o comando status)

git status -s

A saída é uma lista com as alterações, que se parece com isso:

 M test1/test11/t1.txt
    
por 03.10.2011 / 15:45
0

Talvez você esteja mais feliz com algo diferente. Experimente git .

Faça isso como um exemplo:

mkdir a
cd a
git init
touch b
git add . && git commit -m "Empty file"
git status
echo c >> b
git status
git add . && git commit -m "Full file"
git status

git rastreará seus arquivos para você. O comando git status mostrará todos os arquivos que foram modificados desde o último commit.

    
por 03.10.2011 / 18:30
0

Isso é semelhante a rsync: mostra quando arquivos mais novos no destino devem ser sobrescritos (perguntados depois, mas não duplicados).

Como indicado na pergunta, "diff -q -r" pode requerer algum processamento para ser útil. A questão não especificou a forma da saída; as respostas dão diferentes tipos de relatórios.

rsync é uma ferramenta útil para este propósito porque é muito mais rápida que diff . No entanto, a solução sugerida por @nils é muito mais detalhada (e lista mais arquivos) do que as diferenças reais entre as árvores de diretório antigas / novas. Por exemplo, comparando isso com o script que escrevi para essa resposta e executando nos mesmos dados,

  • @nils answer produz 605 linhas (aparentemente porque inclui alterações diretório ),
  • "diff -q -r" produz 352 linhas após a execução por vários minutos e
  • meu script mostra 252 linhas (arquivos reais alterados, adicionados ou excluídos)

Para fazer com que diff seja responsável por novos arquivos, você também precisa da opção -N (que eu não vejo em nenhuma das respostas sugeridas). No entanto, uma vez que é muito mais lento (ordens de grandeza) do que rsync , melhorar a saída deste último parece ser o caminho a seguir.

Leitura adicional

por 15.06.2016 / 03:53
0

Eu sempre fui parcial ao sha1sum (ou mesmo md5sum; neste contexto é bastante seguro).

find . -type f -print0 | xargs -0 md5sum | sort -k2 > /tmp/before
# don't miss the "sort" in there; it's important

# (later)
find . -type f -print0 | xargs -0 md5sum | sort -k2 > /tmp/after
vimdiff /tmp/before /tmp/after
# or whatever diff tool you like, even "diff -u"

Às vezes - como se você tivesse muitos arquivos sendo renomeados ou movidos - ordenar no primeiro campo e então fazer o diff poderia ajudar, mas a maior parte do tempo isso é bom o suficiente.

Observe que, em comparação com alguns dos outros métodos, isso tem a vantagem de não ser necessário manter uma cópia dos arquivos "antes"; somente o arquivo de saída md5sum.

    
por 14.03.2019 / 15:30