2020-04-19   c980f7b6

面倒なファイル名は付けずに8文字IDで管理しよう

みなさんはこんな感じのファイル管理を見たことはないでしょうか?

  • レイアウト_トップ画面.xlsx
  • レイアウト_登録画面.xlsx
  • レイアウト_トップ画面_20190101 提出版.xlsx
  • レイアウト_登録画面_20190101 提出版.xlsx

バージョン管理システムが導入されていないオフィスにいたとき、作業用 PC の作業ディレクトリ内がこんな感じでした(笑)。 ちなみにいまはバージョン管理システムが導入されており、少し作業効率が改善されたのを覚えています。 とはいっても、バージョン管理システムもその人の使い方次第で ↑ のような状況になってしまうのですが……

Web サイトやプレゼンテーション資料を作るときなど、画像素材をいろんなところから収集してくるときがあります。 すると、ファイル名は「猫の画像 1.jpg」「ScreenShot20190101-1.png」「12de944eff….jpg」など、ファイル名の規則性がバラバラで意味を成さなくなります。 そんなときに、ファイル名を8 文字 IDに一括で置き換えて統一する方法を紹介します。

    mvhash コマンドを作ってみた

    Linux や Mac をお使いであれば、以下のシェルスクリプトを導入することで 8 文字 ID へ変換できます。

    上記シェルスクリプトを mvhash というファイル名で /usr/localbin などのパスが通っているところに配置します。 そして、以下のようにすれば 8 文字 ID に変換されます。

    $ ls
      82f231898694e893389f7fc7f0d4b2ae1ddfb69ed2d59295603593c8529db673.jpg
      ScreenShot20190101-1.png
      猫の画像1.jpg
    
    $ mvhash *.jpg *.png
      82f231898694e893389f7fc7f0d4b2ae1ddfb69ed2d59295603593c8529db673.jpg -> fff32890.jpg
      猫の画像1.jpg -> 9018ac4f.jpg
      ScreenShot20190101-1.png -> 9ee84598.png
    
    $ ls
      9018ac4f.jpg  9ee84598.png  fff32890.jpg
    

    せっかくですので、mvhash コマンドの解説をしていきたいと思います!💪

    コマンド引数で while ループ

    while [ $# -ne 0 ]
    do
      # $1 を使った処理
      shift
    done
    

    $ mvhash *.jpg *.png のようにワイルドカードを使ったコマンドを実行するとき、bash がワイルドカードの展開をします。 よって、$ mvhash 猫の画像1.jpg ScreenShot20190101-1.png 82f23189….jpg というコマンドを入力したとみなされます。 mvhash シェルスクリプトでは、与えられたコマンドライン引数ごとにループ処理をさせます。 while 文のループ条件は「引数が 0 でないとき」とし、shift コマンドによって引数のシフトというものをします。 これによって第一引数 $1 が削除され、そのあとに続く引数が $1$2、…となります。

    $ mvhash 猫の画像1.jpg ScreenShot20190101-1.png 82f23189….jpg  (引数3つ)
    ↓shift
    $ mvhash ScreenShot20190101-1.png 82f23189….jpg  (引数2つ)
    ↓shift
    $ mvhash 82f23189….jpg  (引数1つ)
    ↓shift
    $ mvhash  (引数0つのためループ終了)
    

    よって、while ループの中で $1 を使った処理を書けばよいということになります。

    すでに 8 文字 ID 化されている場合はスキップ

    if [[ $1 =~ ^[0-9a-f]{8}\. ]];  then
      shift
      continue
    fi
    

    ファイル名を 8 文字 ID 化するために crc32 というハッシュアルゴリズムを使用するため(後述)、ID は 0〜9 と a〜f の文字を使った 8 文字となります。 そして、8 文字 ID をさらに 8 文字 ID 化できてしまうため、これを防ぐためにスキップ処理を入れています。 if [[ $1 =~ ^[0-9a-f]{8}\. ]] によって、与えられたファイル名がすでに 8 文字 ID であるかを 正規表現マッチして条件分岐します。

    crc32 で 8 文字 ID を生成

    hashed=`crc32 <(echo $1)`.${1##*.}
    echo $1' -> '${hashed}
    mv -i "$1" "${hashed}"
    shift
    

    crc32 コマンドは、指定したファイルの内容をもとに crc32 ハッシュを出力するコマンドです。 しかし、今回は指定したファイルの名前をもとに crc32 ハッシュを取得したいと思います。 そこで、bash の <() という構文を使います。 これは、() 内に書いたコマンドによる標準出力を一時的なファイルとして展開する……というものです。

    なかなか説明が難しいのでデバッグ出力をしてみました。 下記の /dev/fd/63 に注目してください。 /dev/fd/63 という仮想的なファイルには、echo test.png による標準出力、つまり「test.png」という文字列が書き込まれています。 これを crc32 コマンドに送ることで、test.png の中身ではなくファイル名をもとに crc32 ハッシュを生成しています。

    $ echo crc32 <(echo test.png)
      crc32 /dev/fd/63
    

    (余談)「8 文字」ID にした理由

    今回はファイル名を 8 文字の ID に置き換える方法を紹介しました。 実は私にはファイル名は 8 文字!という謎のこだわりを持っています 😤   というのは、だいぶ前にファイル名が 8 文字までしか使えない OS があったからです。 そのなごりからか、Linux のコマンド名も伝統的なものは 8 文字以内に収まっているような気がします。 とはいえさすがにもう 2019 年、docker-compose のように長いコマンド名も普通ですね。 無理やり 8 文字に収めようとすると、直感的でなくなってしまうような気もします。

    この記事の執筆者

    hata6502

    Tomoyuki Hata   Follow @hata6502

    中学生の頃に 6502 という CPU からプログラミングの世界に入りました。 C/C++ で1から作ることも好きですが、最近は Web 系に移行しつつあります。