複数のファイルの文字コードを一括変換する

Linuxマシンをアップグレードしたときや、古いWindowsから持ってきたファイルなど、こうした古いドキュメントの文字コードEUCSJISで、日本語が文字化けして読めないなんてことがよくある。

今時はWindowsでもMacでも、またUbuntuなどのLinux系でも、文字コードUTF-8が標準になってきている。また最近ずっと使っているAtomエディターなど、最近のはUTF-8のデータでないと読めない。

しかし1個や2個のテキストファイルならGeditなどの、文字コード変換して保存できる素なエディターで上書き保存すればいいが、ファイル数がいっぱいだと気が滅入る。

そこでコマンドライン文字コード一括変換。

nkf のインストール

nkf文字コード変換コマンド。まずこれがインストールされてなかったら入れておく。

Ubuntuの場合、sudo apt install nkf でもOKのはずだが、ググるとどうも make しなきゃいけないような話が多くて面倒。
Synapticパッケージマネージャーで nkf と検索しインストールできるので、この方法がおすすめ。

基本的な使い方

nkf オプション ファイル [> 出力ファイル]

主なオプション

  • --overwrite : 文字コードを変換し元のファイルに上書き保存
  • --version : バージョン情報
  • -w : 文字コードをUTF8に指定
  • -e : EUCに変換
  • -s : Windowsで使われているSJISに変換
  • -j : JISコード(ISO-2022-JP)に変換
  • -g : 自動判別の結果を表示

少し詳しい使い方

文字コードをチェックするだけなら-gオプションでOK

$ nkf -g old.txt
EUC-JP

nkf文字コードを指定して読み込むだけなら、標準出力に表示するだけでファイルを書き出すわけではない。書き出す場合は新しいファイル名を明示してリダイレクトする。

書き出すファイル名を指定してEUCUTF-8に変換

$ nkf -w old.txt > new.txt
$ nkf -g new.txt
UTF-8

ただしこの方法で同じファイルに上書き保存はできない(下記のように--overwriteオプションが必要)。

UTF-8文字コード変換して上書き

nkf -w --overwrite some.txt

次のようなやり方はバツ。上書きするときは--overwriteが必須。シェルのリダイレクトでは空のファイルになってしまうので注意。

$ nkf -w some.txt > some.txt

実際は nkf コマンドに何もオプションを付けずにファイルを読み込ませても、文字コードを自動判別し標準出力に書き出す。

ただ文字コードの自動判別がうまくいかない時がある。これはエディターとかでもたまにある。変な文字が入っていたりとかで。 そういう場合に文字コードを大文字オプションで強制指定して読み込ませる方法もある。

大文字入力指定のオプション

  • -E : EUCコードを入力
  • -S : Shift-JISコードを入力
  • -W : UTF-8コードを入力

例: EUC で読み込んで UTF-8 で表示

nkf -E -w --overwrite some.txt

改行コードの変換オプションもある

  • -Lu : UNIX形式(LF)に変換
  • -Lw : Windows形式(CRLF)に変換
  • -Lm : MAC形式(CR)に変換

find でファイル名リストアップ、パイプで nkf へ渡す

ディレクトリ直下にたくさんのファイルがあるとして、それらの文字コードを一括変換したい。find で変換したいファイルをリストアップし、パイプで nkf に渡せばよさそうだが、これでは何も起きない。リストアップされたファイル名の一つ一つを引数にして nkf を実行してくれる仕組みが必要だ。

そこで xargs コマンドを使う。

xargs についてはこちらが詳しい。 【 xargs 】コマンド――コマンドラインを作成して実行する

結果次のようなコマンドラインでファイルの文字コード一括変換ができる。

$ find . -type f -print0 | xargs -0 nkf --overwrite -w -Lu

findコマンドで、カレントディレクトリ「.」から通常ファイル-type fを探索し出力。-print0オプションを使う。

xargs の-0オプションは、長いオプション表記だと--null、入力を空白や改行ではなくNULL文字で区切るもの。

これでカレントディレクトリ下の通常ファイルが、UTF-8UNIX改行形式に変換されて上書き保存される。

ファイル数が200や300でも一瞬で終わる。

find の-print0と xargs の-0オプションはセットで使う

コマンドラインの勉強ついでにNULL文字区切りオプションの件。

find コマンドの-print0オプションは、見つけたファイルのフルパスをNULL区切りで出力するもの。また xargs の-0オプションは、入力を空白や改行ではなくNULL文字で区切って引数のコマンドに渡すもの。

これらのオプションが必要な理由は次の例で分かる。

$ touch Hello_World.txt Hello\ World.txt
$ ls
Hello World.txt  Hello_World.txt

"Hello_World.txt" というアンダーバーで単語を区切ったテキストファイルと、
"Hello World.txt"というスペースで単語を区切ったテキストファイルの2つがある。

区切り文字を考慮しないで find と xargs を連携させると、

$ find . -type f | xargs ls -l
ls: './Hello' にアクセスできません: そのようなファイルやディレクトリはありません
ls: 'World.txt' にアクセスできません: そのようなファイルやディレクトリはありません
-rw-rw-r-- 1 jgb jgb 0  98 20:31 ./Hello_World.txt

ファイル名に空白文字があるほうは、”Hello” と "World.txt" の2つに分割されて ls に渡されるのでエラーになっている。

また find の -print0オプションだけだと、find はNULL文字区切りでファイル名の列を渡すが、xargs はそれをファイル名区切りと認識しないので次のような別の形のエラーになる。

$ find . -type f -print0 | xargs ls -l
xargs: 警告: NUL 文字が入力にあります。これは引数のリストとして渡すことができません。 --null オプションを使おうとしているのですか?
ls: 'World.txt' にアクセスできません: そのようなファイルやディレクトリはありません
-rw-rw-r-- 1 jgb jgb 0  98 20:31 ./Hello_World.txt

逆に xargs の -0オプションだけだと、find の出力にNULL文字が無いので1行続きの './Hello_World.txt'$'\n''./Hello World.txt'$'\n' という出力が ls に渡されたエラーになる。

$ find . -type f | xargs -0 ls -l
ls: './Hello_World.txt'$'\n''./Hello World.txt'$'\n' にアクセスできません: そのようなファイルやディレクトリはありません

ファイル名にスペースや改行などの空白文字が使われている場合、xargs はそれらを区切りとした別々のファイルとして認識してしまうので、find が読みだしたファイル名の出力をNULL文字で区切り、それをxargs にそのNULL文字区切りがファイル名の区切りであると認識させて渡す必要がある。

両方のNULL文字オプションを揃えて使うと上手く行く。

$ find . -type f -print0 | xargs -0 ls -l
-rw-rw-r-- 1 jgb jgb 0  98 20:29 ./Hello World.txt
-rw-rw-r-- 1 jgb jgb 0  98 20:31 ./Hello_World.txt

だから find の-print0と xargs の-0はセットで必要だ。


【参考URL】