Bashシェルスクリプトで空ディレクトリを再帰的に検索し削除する

Bash,Linux,Mac

はじめに

開発が進むと、アプリケーション内に不要な空ディレクトリが誕生することがあります。
そのまま残り続けていると、本当に必要なディレクトリなのかわからなくなることがあります。
あるいはサーバのデータディレクトリ、ワークディレクトリも削除処理が実装されていない場合に不要な空ディレクトリが残ってしまう場合があります。

そんなときな空ディレクトリを再帰的に検索して一気に削除してしまいましょう。

シェルで実現するためのかんたんなワンライナーを紹介します。

検証環境

$ uname -moi
x86_64 x86_64 GNU/Linux

$ head -n 2 /etc/os-release
NAME="CentOS Linux"
VERSION="7 (Core)"

$ bash -version | head -n 1
GNU bash, version 4.2.46(2)-release (x86_64-redhat-linux-gnu)

$ find --version | head -n 2
find (GNU findutils) 4.5.11
Copyright (C) 2012 Free Software Foundation, Inc.

動作確認のための準備

動作確認のために適当なディレクトリツリーを作成しておきます。

# 適当な作業ディレクトリを作成
$ mkdir work/ && cd work/

# ディレクトリとファイルを作成
$ mkdir -p a/d/
$ mkdir -p b/e/
$ mkdir -p b/f/
$ mkdir -p b/g/h/
$ mkdir -p b/g/i/j/
$ mkdir -p "c c/"

$ touch a/d/hoge.txt
$ touch b/f/hoge.txt

tree コマンドで、作成されたディレクトリツリーを確認してみます。

$ tree
.
|-- a
|   `-- d
|       `-- hoge.txt
|-- b
|   |-- e
|   |-- f
|   |   `-- hoge.txt
|   `-- g
|       |-- h
|       `-- i
|           `-- j
`-- c\ c

10 directories, 2 files

上記から分かる通り、以下のディレクトリを空ディレクトリとして削除したいです。

  • ./b/e/
  • ./b/g
  • ./c c/

方法その 1 : find コマンドと xargs コマンドを組み合わせる

残念ながら、空ディレクトリの検索は ディレクトリツリーの末端ディレクトリ しか対象になりません。

どういうことかというと ./b/g ディレクトリ以降は空のディレクトリしかないため、こちらのディレクトリも削除したいのですが、残念ながら検索結果として出力されません

末端ディレクトリを削除した後であれば見つかります。

find コマンドで空のディレクトリを検出し、結果を xargs コマンドで 1 つずつ削除すればいいわけです。
ちゅうしなければ行けない点は、以下の過去エントリで取り上げています。

ポイントは 3 点。

  • ディレクトリパスにスペースが含まれていても正しく動作するように、先程の find コマンドに -print0 オプションを付与
  • xargs コマンドに -0 オプションを付け、 find -print0 で渡ってきたディレクトリリストを null 文字で区切る
  • xargs コマンドに -r オプションを付け、 find の結果 1 ディレクトリも見つからなかった場合でも正しく動作するようにする
$ find . -type d -empty -print0 | xargs -0 -r -n 1 rmdir

# ディレクトリツリーを表示してみる
$ tree
.
|-- a
|   `-- d
|       `-- hoge.txt
`-- b
    |-- f
    |   `-- hoge.txt
    `-- g
        `-- i

6 directories, 2 files

./b/g ディレクトリは空ですがまだ残っています。
これを削除するためには何度か今回のコマンドを実行してやる必要があります。

# 再度削除
$ find . -type d -empty -print0 | xargs -0 -r -n 1 rmdir

$ tree
.
|-- a
|   `-- d
|       `-- hoge.txt
`-- b
    |-- f
    |   `-- hoge.txt
    `-- g

5 directories, 2 files

# 再々度削除
$ find . -type d -empty -print0 | xargs -0 -r -n 1 rmdir

$ tree
.
|-- a
|   `-- d
|       `-- hoge.txt
`-- b
    `-- f
        `-- hoge.txt

4 directories, 2 files

何回繰り返せばよいのかわからないため、繰り返しの終了条件をうまく定義してやる必要がありますね。

方法その 2 : find コマンドの -delete オプションを利用する

もう一つの方法として、 find コマンドの -delete オプションを利用するというものがあります。
こちらはどういう仕組みなのか、空ディレクトリのみを内包するディレクトリがあった場合にまとめて削除してくれますし、
パスにスペースが含まれていたとしてもあんじょう取り扱ってくれます。

$ find . -type d -empty -delete

$ tree
.
|-- a
|   `-- d
|       `-- hoge.txt
`-- b
    `-- f
        `-- hoge.txt

4 directories, 2 files

ちなみに、 -delete オプションが利用できない find バージョンも有るようなので注意が必要です。

$ find --version | head -n 2
find (GNU findutils) 4.5.11
Copyright (C) 2012 Free Software Foundation, Inc.

ひとこと

面倒な点もよしなにやってくれるので、 find コマンド + -delete オプションの方法を採用するのが容易そうですね。

Bash,Linux,Mac