ferramentas diff linux: crie uma lista de arquivos modificados


14

Como crio uma lista de arquivos modificados programaticamente usando as ferramentas de linha de comando do linux? Não estou interessado na diferença em nenhum arquivo em particular (delta, patch). Eu só quero ter uma lista de arquivos novos ou modificados em comparação com o lançamento anterior do produto. Para que eu possa publicar uma nova atualização do produto.

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


o que é um exemplo de saída "conveniente"?
Frogstarr78

Respostas:


8

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

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

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


13

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

Saída absolutamente horrível e ilegível, cheia de informações sem sentido dizendo o Only inque aparece mesmo que os diretórios sejam cópias ideais. Precisava comparar as alterações com uma revisão antiga e acabar fazendo o download da revisão inteira em um diretório separado e usando as ferramentas SVN padrão para comparar. Isso parece ser a única maneira de ir ...
Hi-Angel

3

O diffutilspacote inclui uma lsdiffferramenta. Apenas passe a saída de diff -upara lsdiff:

diff -u --other-diff-options path1 path2 | lsdiff

Boa sugestão, obrigado. Estava no patchutilspacote para mim (CentOS 5.x).
Steve Kehlet

Sim, pacote patchutils para Ubuntu / Debian também.
Artfulrobot

1

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


1

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

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

Se precisar excluir alguns arquivos como arquivos de objeto ou de biblioteca, você pode usar:

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

1

Para criar uma lista de arquivos novos ou modificados de forma programática, a melhor solução que eu poderia encontrar é usar rsync , sort 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 dois lançamentos dokuwiki para ver quais arquivos foram alterados e quais foram criados recentemente.

Buscamos os alcatrões com o 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 os arquivos recém-criados, como mostra a comparação do rsync e do diff aqui:

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

produz a seguinte saída:

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

A execução do rsync apenas em uma direção perde os arquivos recém-criados e, ao contrário, os arquivos excluídos são comparados, compare a saída do diff:

diff -qr old/ new/

produz a seguinte saída:

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 o 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.txtforam perdidos inicialmente:

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

produz a seguinte saída:

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 estes argumentos:

  • -r "recursar 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 tempo e tamanho da modificação",
  • -n para "executar uma avaliação sem alterações feitas" e
  • --out-format="%n" para "gerar atualizações usando o FORMAT especificado", que é "% n" aqui apenas para o nome do arquivo

A saída (lista de arquivos) de rsyncambas as direções é combinada e classificada usando sort, e essa lista classificada é então condensada removendo todas as duplicatas comuniq


0

Você deve obter o resultado desejado usando:

diff -r --brief dir1/ dir2/

0

Isso pode fazer o truque:

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\0' | sort -z | quote_shell) \
        <(cd -- "$2" && find . -printf '%P\0' | sort -z | quote_shell)
}

0

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

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

Talvez algo parecido com isto:

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, portanto, o exemplo acima é "pseudocódigo inspirado no bash" ...


Vamos dar outra chance, mas com git

Crie alguns arquivos de exemplo para brincar

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, insira o dir e importe o dir1

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

Saia e modifique dir1 (para que se torne seu dir2)

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

Então entre no 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

0

Talvez você esteja mais feliz com algo diferente. Tente 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

gitrastreará seus arquivos para você. O comando git statusmostrará todos os arquivos que foram modificados desde a última confirmação.


0

Isso é semelhante ao rsync: show quando um arquivo mais recente no destino deve ser substituído (solicitado mais tarde, embora não seja um duplicado).

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

rsyncé uma ferramenta útil para esse fim, porque é muito mais rápido 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 antigas / novas árvores de diretório. Por exemplo, comparando isso com o script que escrevi para essa resposta e executando nos mesmos dados,

  • A resposta @nils produz 605 linhas (aparentemente porque inclui alterações de 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 diffexplicar adequadamente os novos arquivos, você também precisa da -Nopção (que não vejo em nenhuma das respostas sugeridas). No entanto, como é muito mais lento (ordens de magnitude) do que rsyncmelhorar o resultado deste último parece o caminho a percorrer.

Leitura adicional


0

Eu sempre fui parcial em relação ao sha1sum (ou até ao md5sum; nesse 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ê tiver muitos arquivos sendo renomeados ou movidos - a classificação no primeiro campo e a execução do diff podem ajudar, mas na maioria das vezes isso é bom o suficiente.

Observe que, comparado a alguns dos outros métodos, isso tem a vantagem de que você não precisa manter uma cópia dos arquivos "anteriores"; somente o arquivo de saída md5sum.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.