複数のファイルの文字コードを一括変換する
Linuxマシンをアップグレードしたときや、古いWindowsから持ってきたファイルなど、こうした古いドキュメントの文字コードがEUCやSJISで、日本語が文字化けして読めないなんてことがよくある。
今時は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 は文字コードを指定して読み込むだけなら、標準出力に表示するだけでファイルを書き出すわけではない。書き出す場合は新しいファイル名を明示してリダイレクトする。
$ nkf -w old.txt > new.txt $ nkf -g new.txt UTF-8
ただしこの方法で同じファイルに上書き保存はできない(下記のように--overwrite
オプションが必要)。
nkf -w --overwrite some.txt
次のようなやり方はバツ。上書きするときは--overwrite
が必須。シェルのリダイレクトでは空のファイルになってしまうので注意。
$ nkf -w some.txt > some.txt
実際は nkf コマンドに何もオプションを付けずにファイルを読み込ませても、文字コードを自動判別し標準出力に書き出す。
ただ文字コードの自動判別がうまくいかない時がある。これはエディターとかでもたまにある。変な文字が入っていたりとかで。 そういう場合に文字コードを大文字オプションで強制指定して読み込ませる方法もある。
大文字入力指定のオプション
nkf -E -w --overwrite some.txt
改行コードの変換オプションもある
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-8、UNIX改行形式に変換されて上書き保存される。
ファイル数が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 9月 8 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 9月 8 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 9月 8 20:29 ./Hello World.txt -rw-rw-r-- 1 jgb jgb 0 9月 8 20:31 ./Hello_World.txt
だから find の-print0
と xargs の-0
はセットで必要だ。
【参考URL】
- findとxargsコマンドで-print0オプションを使う理由(改) https://qiita.com/maskedw/items/2dfdf6fa7eee991ddc45
- print0 コマンド https://qiita.com/tbrook/items/ce1bfd03a25f6d5080b5
- findコマンド(応用編その2)――検索したファイルを指定したフォーマットで表示する http://www.atmarkit.co.jp/ait/articles/1607/20/news024.html