Linuxコマンドラインツールを使用して、変更されたファイルのリストをプログラムで作成するにはどうすればよいですか?特定のファイル(デルタ、パッチ)の違いには興味がありません。以前の製品リリースと比較した新しいファイルまたは変更されたファイルのリストが欲しいだけです。新しい製品アップデートを公開できるようにします。
更新:diff -qr
は非常に便利な出力を生成しません。 diff -qr
の出力も処理する必要があります。もっと良い方法はありますか?
私はこれのための簡単なアプローチを持っています:rsync-previewモードを使用します:
rsync -aHSvn --delete old_dir/ new-dir/
そのコマンドで「削除する」と表示されているファイルが「新しい」ファイルになります。転送される他のものは、何らかの方法で変更されています。詳細については、rsync-man-pageを参照してください。
diff tooolを使用できます。オプション-qおよび-rを参照してください
-q --brief
Output only whether files differ.
-r --recursive
Recursively compare any subdirectories found.
例:
diff -qr dir1 dir2
diffutils
パッケージには、lsdiff
ツールが含まれています。 diff -u
の出力をlsdiffに渡すだけです。
diff -u --other-diff-options path1 path2 | lsdiff
変更されたファイルの名前のみを取得するには、次のコマンドを使用します。
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/'
プログラムで新しいファイルまたは変更されたファイルのリストを作成するには、rsync、sort、および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
を使用してすべての重複を削除することで圧縮されます。
各更新時にファイルに触れるだけで、それ以降にfind /tree/location -newer /last/update/file -print
で変更されたファイルを見つけることができます
これは、rsync:宛先の新しいファイルを上書きするタイミングを表示と似ています(後で確認しますが、重複はしません)。
質問に示されているように、「diff -q -r」を使用するには、何らかの処理が必要な場合があります。質問は出力の形式を指定しませんでした。答えはさまざまな種類のレポートを提供します。
rsync
よりもmuch高速であるため、diff
はこの目的に役立つツールです。ただし、@ nilsによって提案された解決策は、古いディレクトリツリーと新しいディレクトリツリーの実際の違いよりもはるかに詳細(およびより多くのファイルをリスト)です。たとえば、それを、その回答のために書いたスクリプトと比較し、同じデータで実行します。
diff
にnewファイルを正しく反映させるには、-N
オプションも同様です(推奨される回答には表示されません)。ただし、rsync
よりもはるかに遅い(桁違い)ので、後者の出力を改善するのが適切です。
参考文献
私はいつも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出力ファイルのみ。
あなたは以下を使用して望ましい結果を得るはずです:
diff -r --brief dir1/ dir2/
通常は、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
これ トリックを行うかもしれません:
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)
}
たぶん、あなたは別の何かで幸せになるでしょう。 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
は、最後のコミット以降に変更されたすべてのファイルを表示します。