[Linux]大量のファイル名のリネームをコマンド一発で実行するヒント

ファイルのリネームって度々発生します。
数が多くなると面倒でスクリプト組んだりしてたのですが、Linuxコマンド一発でできることを知り、感動しましたのでやり方を残します。

環境

  • MacOS 10.13.6

Mac OSにLinuxのsedを入れて使う

今回sedというコマンドを使います。
ただ、Macに標準で入っているsedは、Linuxのsed(GNUのsed)と微妙に挙動が違うので、LinuxのGNU版sedを入れて使います。

# homebrew経由でGNU版sedをインストール
brew install gnu-sed

# sedコマンドでgsedが使われるように設定(zshの場合)
vi ~/.zshrc

alias sed="gsed"

# 設定を反映
source ~/.zshrc

参考

お題

[field1]_[field2].tsv
というファイル名のデータを
[field2]_[field1].tsv
に直す必要がある、というケース。

具体例

a_20190601.tsv
a_20190602.tsv
a_20190603.tsv
b_20190601.tsv
b_20190602.tsv
b_20190603.tsv
c_20190601.tsv
c_20190602.tsv
c_20190603.tsv

20190601_a.tsv
20190602_a.tsv
20190603_a.tsv
20190601_b.tsv
20190602_b.tsv
20190603_b.tsv
20190601_c.tsv
20190602_c.tsv
20190603_c.tsv

に直す。

数ファイルならまだしも、数百のファイルを一つ一つリネームなんてやってられない。
そんなときに。

一発コマンド

ls、sed、そしてbashをパイプでつなげて使います。

$ ls -1 | sed -r "s/([a-z]+?)_([0-9]+?)\.tsv/mv & \2_\1.tsv/g" | bash

実行結果

1. 下準備

ディレクトリとファイルの作成

※$マークは省略してます

mkdir sed_sample
cd sed_sample
touch a_20190601.tsv
touch a_20190602.tsv
touch a_20190603.tsv
touch b_20190601.tsv
touch b_20190602.tsv
touch b_20190603.tsv
touch c_20190601.tsv
touch c_20190602.tsv
touch c_20190603.tsv

2. ファイル確認

ls -1

a_20190601.tsv
a_20190602.tsv
a_20190603.tsv
b_20190601.tsv
b_20190602.tsv
b_20190603.tsv
c_20190601.tsv
c_20190602.tsv
c_20190603.tsv

3. コマンド実行

ls -1 | sed -r "s/([a-z]+?)_([0-9]+?)\.tsv/mv & \2_\1.tsv/g" | bash

4. 結果確認

ls -1

20190601_a.tsv
20190601_b.tsv
20190601_c.tsv
20190602_a.tsv
20190602_b.tsv
20190602_c.tsv
20190603_a.tsv
20190603_b.tsv
20190603_c.tsv

うまくいきました。

解説

ls -1 | sed -r "s/([a-z]+?)_([0-9]+?)\.tsv/mv & \2_\1.tsv/g" | bash

上記コマンドを分解すると、
1. lsで、カレントディレクトリにあるファイル一覧を出力
2. パイプで1の出力結果を受け取り、sedで置換し、mv [元ファイル名] [新ファイル名]のテキストを出力
3. パイプで2の出力結果を受け取り、bashで実行
という流れになっています。

sedの解説

sedの要点だけ解説します。
sedは正規表現を使って特定の文字列の抽出・置換が行える便利なコマンドで、
sed "s/置換前/置換後/"という形式で記述します。

置換前の部分

([a-z]+?)_ : アルファベット小文字1文字以上の文字列を、_(アンダースコア)が出現するまで抽出し、キャプチャ
([0-9]+?)\.tsv : 数字1文字以上の文字列を、.tsvが出現するまで抽出し、キャプチャ

置換後の部分

& : 置換前のテキストをそのまま出力
\2_\1.tsv : キャプチャした文字列を、2番目のキャプチャ、1番目のキャプチャの順に出力

というように、置換を実行しています。

試しに、1. 下準備をしたあとにsedまでのコマンドを実行してみます。

ls -1 | sed -r "s/([a-z]+?)_([0-9]+?)\.tsv/mv & \2_\1.tsv/g"

mv a_20190601.tsv 20190601_a.tsv
mv a_20190602.tsv 20190602_a.tsv
mv a_20190603.tsv 20190603_a.tsv
mv b_20190601.tsv 20190601_b.tsv
mv b_20190602.tsv 20190602_b.tsv
mv b_20190603.tsv 20190603_b.tsv
mv c_20190601.tsv 20190601_c.tsv
mv c_20190602.tsv 20190602_c.tsv
mv c_20190603.tsv 20190603_c.tsv

置換の結果、上記のような文字列が生成されます。

最後に上記の文字列をパイプでbashコマンドに渡し、Linuxコマンドとして実行することで、一括置換が実現できます。

まとめ

Linuxコマンドと正規表現を使えると面倒な単純作業から解放されるので、使いこなせるようになりたいものです。

今回のコマンドも、sedの正規表現の部分はファイル名の書式によって個別でカスタマイズする必要があるので、頑張って正規表現覚えましょう。

参考