バックナンバー

重複したファイルを削除する

BUW(Bash on Ubuntu on Windows)で重複したファイルを探すにはいくつかの方法がある。例えば前回使用した「find」コマンドで検索したファイルを名前やサイズを用いて比較すればよい。だが、この場合ファイルの内容が同じか否か判断することは難しいだろう。

そのため、MD5チェックサム情報を作成する「md5sum」コマンドを併用し、各ファイルのチェックサム情報を比較。そして、同一のファイルを削除対象にすればよい。このロジックであればファイル名が異なる場合でも、出力するチェックサムは同じ値になるため、適切に重複ファイルを拾い上げることが可能だ。

もちろんシェルスクリプトで粛々とコードを書くこともできるが、コードは短ければ短いほど後から読み返すのも簡単である。つまり保守性も向上し、後から異なるシェルスクリプトにも流用しやすいはずだ。そこで、指定したフォルダー(ディレクトリ)をスキャンし、重複したファイルの一覧表示や削除といった操作を行う「fdupes」コマンドを使おう。Adrian Lopez氏がMITライセンス下で公開しているオープンソースだ。Ubuntuには標準でインストールされていないため、「sudo apt-get install fdupes -y」と実行して、あらかじめパッケージをインストールしておこう。

プロンプトに「sudo apt-get install fdupes -y」と入力して[Enter]キーを押す。もちろん事前に「sudo apt-get update」を実行してリポジトリを更新し、必要であればパスワードを入力する

こちらがfdupesコマンドのオプション内容。シンボリックリンクやハードリンクも検索対象に加えるオプションが確認できる

使い方は簡単だ。コマンドラインから「fdupes -r {対象フォルダー}」と実行すればよい。これで各ファイルのMD5チェックサムを確認して、同一であるファイルをリストアップする。ちょうど冒頭で述べたロジックを1つのコマンドで実行する形だ。

「fdupes -r {対象フォルダー}」と入力して[Enter]キーを押せば、同一内容のファイルがそれぞれ表示される

しかし、上図のとおりマッチする両方のファイルがリストアップされるため、一方を残さなければならない場合は不具合が生じてしまう。そこで「-f(--omitfirst)」オプションを併用しよう。こちらは最初にマッチした前方のファイルを削除できる。

オプション「-f」を追加することで、削除対象となるファイルのみを出力できる

本連載読者なら、それでもまだ問題が残ることにお気付きだろう。パス名をご覧になるとお分かりのとおり、今回の検証用に作成したBackupフォルダーが先にヒットしているため、本来は残しておきたい既存フォルダーのファイルが削除対象となってしまう。つまり、そのままfdupesコマンドを使用すると、どのファイルが残り、どのファイルが削除されるのか不明確になってしまうのだ。そこで作成したのが、下記に示したシェルスクリプトである。シェルスクリプトを作成したら、実行権限を与えてからお試し頂きたい。

 #!/bin/bash

 Cmdname="$0"
 MoveDir="/mnt/c/Users/kaz/Desktop/_Delete"
 TmpFile=$MoveDir/foo.txt

 if [ "$#" -ne 1 ]; then
    echo "Usage: ${Cmdname} Directory" 1>&2
    exit 1
 elif [ -d $1 ]; then
    if [ ! -d $MoveDir ]; then
        mkdir $MoveDir
    fi

    fdupes -rf1 $1 > $TmpFile
    if [ -s $TmpFile ]; then
        while read File;
            do mv $File -t $MoveDir
        done < $TmpFile
    else
        echo    'no duplicate files.'
    fi
    rm $TmpFile
 fi

基本的な構造は前回と似通っているが、ポイントは15行目。今回紹介したfdupesコマンドの結果を変数TmpFileで定義したファイルに出力している。その際出力結果から空行を削除するオプション「-1」を追加した。16行目からは出力ファイルに重複ファイルの内容が記述されている場合は18行目でmvコマンドを実行している。また、ゼロバイトのファイルの場合は20~21行目の条件実行として、echoコマンドでメッセージを出力。そして最後に出力ファイルを削除して終了する。もし、移動したファイルの一覧が必要な場合は、23行目のrmコマンドを削除すればよい。

スクリプトを実行した状態。画面上側のTestフォルダーから画面右下側の_Deleteフォルダーに重複ファイルが移動している

一見するとシェルスクリプトの内容は冗長に感じるだろう。単にfdupesコマンドの出力結果を元にファイルを移動させるのであれば、「fdupes -rf $1 | xargs mv -t $MoveDir」とワンライナーで済むと考えるのは正しい。だが、重複ファイルが存在せず、fdupesコマンドの標準出力が空の場合、mvコマンドは「ファイルオペランドがありません」とエラーを発するため、今回はこのような条件分岐を追加することにした。

阿久津良和(Cactus)