Bash

ディレクトリ同士の構成を比較して差分を指摘してくれるBash関数

 例えばレンタルサーバ等でWEBサイトを運用していたりすると、ドメインごとにディレクトリを分けてVirtualHost設定で複数WEBサイトを立ち上げるというケースがしばしばある。とくに小規模なサイトだと、商用環境とステージング環境、開発環境とかをそれぞれ別ディレクトリにしてサブドメインで仮想化しているというケースは多いのではないだろうか。
 そういうケースで、それぞれのディレクトリ内のファイルやサブディレクトリの構成は原則的に一緒なんだけど、ホントに同一構成になっているのか、ディレクトリ間の構成を比較したい時がある。

 まぁ、最適解としては、比較元となるディレクトリの構成全体をgithubのリポジトリ化しておくことだ。そうしておけば、git diffで差分チェック、git pullでコンテンツ同期ということがお手軽にできるようになる。
 しかしWEBサイト等の実際の運用を行っている場合、運営側がアップロードした画像ファイル等のアプリケーションに関わらないリソースは.gitignoreでリポジトリの管理対象外にしていることが多く、それらのリソースを含めた純粋な構成比較をしようとするとgitコマンドだけでは完結できない。
 そこで、2つのディレクトリ間の構成内容を再帰的に比較して、差分があればその内容を出力してくれるBash関数を作ってみた。

.bashrc
# Checksum for comparing file structure between directories
#
# @param {string} directory1(path from current dir)
# @param {string} directory2 (path from current dir)
function dirsum() {
    if [ $# -ne 2 ]; then
        echo "Specify the directory to compare." 1>&2
    else
        CURRENT=$(pwd)
        if [ -d $1 ]; then
            cd $1
            FILES1=$(find . | wc -l)
            CHKSUM1=$(find . | env LC_ALL=C sort | md5sum -b) # easily check
            # CHKSUM1=$((find . -type f -exec md5sum -b {} \; && find .) | env LC_ALL=C sort | md5sum -b) # detailed check
            echo "Checksum hash: $CHKSUM1  Total files found: $FILES1 <- Dir: $1"
        else
            CHKSUM1=""
            echo "Directory: $1 does not exist."
        fi
        cd $CURRENT
        if [ -d $2 ]; then
            cd $2
            FILES2=$(find . | wc -l)
            CHKSUM2=$(find . | env LC_ALL=C sort | md5sum -b) # easily check
            # CHKSUM2=$((find . -type f -exec md5sum -b {} \; && find .) | env LC_ALL=C sort | md5sum -b) # detailed check
            echo "Checksum hash: $CHKSUM2  Total files found: $FILES2 <- Dir: $2"
        else
            CHKSUM2=""
            echo "Directory: $2 does not exist."
        fi
        cd $CURRENT
        if [ -n "$CHKSUM1" -a -n "$CHKSUM2" ]; then
            if [ "$CHKSUM1" = "$CHKSUM2" ]; then
                echo "Checksum hash matched."
            else
                echo "Checksum hash is different."
                diff -r $1 $2
            fi
        fi
    fi
}
alias dirsum=dirsum
alias dsum=dirsum 

 この処理では# easily checkでサブディレクトリ名とファイル名の構成が一致しているかの簡易的なチェックしかしていない。もしファイル内容まで含めた差分を検証したい場合は、# easily checkの行をコメントアウトして、# detailed checkの行を有効にすることで対応できる。ただし、検証するディレクトリ内のファイル数が多いと詳細チェックにはかなりの時間がかかるので、注意が必要だ。

 さて、上記のBash関数を.bashrcに追加して反映させると、dirsumコマンド(ショートハンドのdsumでもOK)で2つのディレクトリ間の差分を検証できるようになる。

ディレクトリ間に差分がない場合
$ dsum www_production www_staging
Checksum hash: 51e38a180b74829e7cb97a7bada2a2a9 *-  Total files found: 8694 <- Dir: www_production
Checksum hash: 51e38a180b74829e7cb97a7bada2a2a9 *-  Total files found: 8694 <- Dir: www_staging
Checksum hash matched.
ディレクトリ間に差分がある場合
$ dsum www_production www_staging
Checksum hash: 51e38a180b74829e7cb97a7bada2a2a9 *-  Total files found: 8694 <- Dir: www_production
Checksum hash: de8161f8f26e014ce7bf7b02ef5845e3 *-  Total files found: 8693 <- Dir: www_staging
Checksum hash is different.
www_production/assets/db-backup のみに存在: My_Site_2019_01_11_1547181637_206b5af_wpdb.zip

例えば、差分があった場合に担当者へメールする等の処理を追加し、cronで定期的に差分検証するようにすれば、WEBサイトの改竄検知にも使えるかもしれない。

参考

md5sum でディレクトリ単位のチェックサム計算等