読者です 読者をやめる 読者になる 読者になる

ほんじゃら堂

めんどくさい仕事をラクにする作業自動化レシピ集

指定フォルダ以下のファイルとフォルダのサイズを一覧表示する

f:id:piro_suke:20160725002531j:plain

PCやサーバの容量がなくなる、というのは案外よくあるもので、

年に1回くらいそんな事態に遭遇しては、

どのフォルダ・ファイルが容量を圧迫してるんだ!

容量を調べるためのコマンドなんだっけ?

とコマンドを探したりツールを探したりする。

Linuxのコマンド

Linuxの場合はduコマンドを駆使することで調べることができる。

du -sh <対象ディレクトリのパス>

このコマンドを実行すると、

lsコマンドのように対象ディレクトリ以下のディレクトリとファイルの

一覧を表示してくれるのだけど、

同時にサブディレクトリも含めた容量を計算して表示してくれる。

やたら容量を使ってるサブディレクトリがあったら、

今度はそのサブディレクトリを対象にduコマンドを実行する、

ということを繰り返すことで、

容量を使っている場所を無駄に使っている場所を突き止めることができる。

Windowsのツールとコマンド

Windowsの場合も

DOSコマンドやPowerShellコマンドでチェックできるようだが、

まだ使ったことはない。下記のサイトにまとまっていた。

orebibou.com

GUIのツールでは、以前FileSumというツールを使ったことがあり、

便利だった記憶がある。

www.vector.co.jp

フォルダ・ファイルサイズチェックスクリプトを書く

上記のコマンドやツールで十分目的を果たせそうなのだけど、

そんなに難しくなさそうなので、自分でも容量表示スクリプトを書いてみる。

処理内容としては:

  • コマンドパラメータとしてチェック対象のフォルダパスを1つ受け取る
  • 対象フォルダパス以下のサブフォルダとファイルの一覧を取得する
  • 各ファイルのサイズを取得する
  • フォルダの容量はそのフォルダ内のサブフォルダとファイルのサイズの合計を設定する
  • フォルダとファイルをサイズとセットでマップにまとめる
  • マップの内容を表示する

という流れが良さそう。

Clojure版フォルダ・ファイルサイズチェックスクリプト

Clojureはfile-seqという、指定パス以下のフォルダとファイルを

java.io.Fileオブジェクトのリストにして返してくれる便利な関数があり、

これが使えそう。

但し、フォルダについては内容を合計したサイズでは出してくれないので、

自分でサブフォルダとファイルのサイズを合計する必要がある。

file-size-checkerという名前でLeiningenプロジェクトを作成した。

project.clj

(defproject file-size-checker "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :dependencies [[org.clojure/clojure "1.8.0"]]
  :main ^:skip-aot file-size-checker.core
  :target-path "target/%s"
  :profiles {:uberjar {:aot :all}})

今回はコアライブラリとjavaの標準パッケージ以外使用しなかった。

core.clj

(ns file-size-checker.core
  (:gen-class))

(defn parent-dir-path-list
  "パスを分解して親フォルダのリストを作成して返す"
  [f target-dir]
  (let [separator (java.io.File/separator)
        path-str (clojure.string/replace (.getPath f) (re-pattern (str (->> target-dir
                                                                            (.getCanonicalFile)
                                                                            (.getParentFile)
                                                                            (.getPath)) separator)) "") 
        sep-path (clojure.string/split path-str (re-pattern separator))]
    (reduce (fn [l d]
              (conj l (str 
                        (if (empty? l) "" (str (last l) separator)) 
                        d))) [] sep-path)))

(defn walk-dir-and-calc-size
  "指定パス内のファイルサイズを計算してパスをキーとしたサイズのマップを返す"
  [dir-path]
  (let [dir (clojure.java.io/file dir-path)
        fs (file-seq dir)]
    (reduce (fn [m path]
              (if (.isDirectory path)
                m   
                (let [sep-path (parent-dir-path-list path dir)]
                  (merge m
                         (into {}
                               (for [f sep-path]
                                 [f (+ (get m f 0) (.length path))])))))) {} fs)))

(defn -main
  [& args]
  (let [dir-path (first args)
        file-size-map (into (sorted-map) (walk-dir-and-calc-size dir-path))]
    (doseq [[f s] file-size-map]
      (println (str f " " s)))))

下記のように実行できる:

lein run <対象パス>
java -jar file-size-checker.jar <対象パス>

Windowsで使用するのが目的で作成したのだけど、

まだWindowsでは使っていない。

Linuxでは動いた。

Windowsでうまく動くようなら、

どこでも使えるファイルサイズチェックツールとして

少なくとも自分の役には立ちそうだ。

おわり

reduce関数は色々な用途で幅広く使えて興味深い。

Clojure Essentials: 入門書では飽き足らない人へ

Clojure Essentials: 入門書では飽き足らない人へ