web-dev-qa-db-ja.com

linux diffツール:変更されたファイルのリストを作成する

Linuxコマンドラインツールを使用して、変更されたファイルのリストをプログラムで作成するにはどうすればよいですか?特定のファイル(デルタ、パッチ)の違いには興味がありません。以前の製品リリースと比較した新しいファイルまたは変更されたファイルのリストが欲しいだけです。新しい製品アップデートを公開できるようにします。

更新:diff -qrは非常に便利な出力を生成しません。 diff -qrの出力も処理する必要があります。もっと良い方法はありますか?

14
Alpha Sisyphus

私はこれのための簡単なアプローチを持っています:rsync-previewモードを使用します:

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

そのコマンドで「削除する」と表示されているファイルが「新しい」ファイルになります。転送される他のものは、何らかの方法で変更されています。詳細については、rsync-man-pageを参照してください。

8
Nils

diff tooolを使用できます。オプション-qおよび-rを参照してください

-q  --brief
Output only whether files differ.

-r  --recursive
Recursively compare any subdirectories found.

例:

diff -qr dir1 dir2
13
stivlo

diffutilsパッケージには、lsdiffツールが含まれています。 diff -uの出力をlsdiffに渡すだけです。

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

変更されたファイルの名前のみを取得するには、次のコマンドを使用します。

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

一部のファイルをオブジェクトファイルまたはライブラリファイルとして除外する必要がある場合は、以下を使用できます。

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

プログラムで新しいファイルまたは変更されたファイルのリストを作成するには、rsyncsort、およびniqを使用することが最善の解決策です。

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

この例で説明します。2つのdokuwikiリリースを比較して、変更されたファイルと新しく作成されたファイルを確認します。

Wgetを使用してtarをフェッチし、old/および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

Rsyncとdiffの比較がここに示すように、rsyncを一方向で実行すると、新しく作成されたファイルを見逃す可能性があります。

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

は次の出力を生成します:

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

一方向でのみrsyncを実行すると、新しく作成されたファイルが失われます。逆に、削除されたファイルが失われる場合は、diffの出力を比較します。

diff -qr old/ new/

は次の出力を生成します:

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

両方の方法でrsyncを実行し、出力をソートして重複を削除すると、最初にディレクトリdata/pages/playground/とファイルdata/pages/playground/playground.txtが見つからなかったことがわかります。

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

は次の出力を生成します:

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はこれらの引数で実行されます:

  • -rを "ディレクトリに再帰する"、
  • -cは、同じサイズのファイルも比較し、「mod-timeとサイズではなく、チェックサムに基づいてスキップする」だけです。
  • -nを使用して、「変更を加えずに試用を実行します」。
  • --out-format="%n"から「指定されたFORMATを使用して更新を出力する」。これは、ここではファイル名のみの「%n」です

両方向のrsyncの出力(ファイルのリスト)が結合され、sortを使用してソートされます。次に、このソートされたリストは、uniqを使用してすべての重複を削除することで圧縮されます。

1
iolsmit

各更新時にファイルに触れるだけで、それ以降にfind /tree/location -newer /last/update/file -printで変更されたファイルを見つけることができます

1
Paul Tomblin

これは、rsync:宛先の新しいファイルを上書きするタイミングを表示と似ています(後で確認しますが、重複はしません)。

質問に示されているように、「diff -q -r」を使用するには、何らかの処理が必要な場合があります。質問は出力の形式を指定しませんでした。答えはさまざまな種類のレポートを提供します。

rsyncよりもmuch高速であるため、diffはこの目的に役立つツールです。ただし、@ nilsによって提案された解決策は、古いディレクトリツリーと新しいディレクトリツリーの実際の違いよりもはるかに詳細(およびより多くのファイルをリスト)です。たとえば、それを、その回答のために書いたスクリプトと比較し、同じデータで実行します。

  • @nilsの回答は605行を生成します(どうやらディレクトリの変更が含まれているため)、
  • "diff -q -r"は、数分間実行した後に352行を生成します。
  • 私のスクリプトは252行を表示します(actualファイルが変更、追加、または削除されました)

diffnewファイルを正しく反映させるには、-Nオプションも同様です(推奨される回答には表示されません)。ただし、rsyncよりもはるかに遅い(桁違い)ので、後者の出力を改善するのが適切です。

参考文献

0
Thomas Dickey

私はいつもsha1sum(またはmd5sumでさえ、このコンテキストではそれは非常に安全です)に部分的でした。

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"

名前を変更したり移動したりするファイルが多すぎる場合など、最初のフィールドでソートしてからdiffを実行すると役立つ場合がありますが、ほとんどの場合これで十分です。

他のいくつかの方法と比較して、これには「前」のファイルのコピーを保持する必要がないという利点があることに注意してください。 md5sum出力ファイルのみ。

0
sitaram

あなたは以下を使用して望ましい結果を得るはずです:

diff -r --brief dir1/ dir2/
0
Marcin Gil

通常は、Subversionやgitなどのバージョン管理システムにファイルを配置します。これらは、箱から出してすぐに実行できるためです。

ただし、dir1でforループを使用して簡単なスクリプトを実行し、すべてのファイルをdir2のファイルと比較できます。 forループは、diffの終了コードを調べて、ファイルが異なるかどうかを確認できます。

多分このようなもの:

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

注:スクリプトはテストされていないため、上記の例は「bashに触発された疑似コード」です...


もう一度やってみましょうが、git

一緒にプレイするサンプルファイルを作成する

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

次に、dirを入力してdir1をインポートします。

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

外に出てdir1を変更します(そのため、dir2になります)。

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

次にgit dirに移動し、新しいディレクトリをインポートします

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

変更点をgitに尋ねます(statusコマンドを使用)

git status -s

出力は、次のように変更されたリストです。

 M test1/test11/t1.txt
0
Johan

これ トリックを行うかもしれません:

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
l0b0

たぶん、あなたは別の何かで幸せになるでしょう。 git を試してください。

例としてこれを行います:

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はファイルを追跡します。コマンド git statusは、最後のコミット以降に変更されたすべてのファイルを表示します。

0
bahamat