sed・grepで濁点と改行をまともに扱う方法
#sed 's/^/-/g' # 行頭に-を付加する #sed 's/$/-/g' # 行末に-を付加する #sed 's/\(xxxx\)/[\1]/g' # xxxxを[]で囲う #sed 's/xxxx/oooo/g' # xxxxをooooに置き換える
grep -i 'xxxx' #-i 大文字と小文字を区別しない #-v パターンに一致しない行を表示する #-n パターンに一致した行のファイル内での行番号を表示する #-c パターンに一致した行の行数のみを出力する #-b パターンに一致した行の先頭からのバイト数を表示する
- ところが、自分は生粋の日本人。OSXもアプリケーションも日本語環境で利用している。一つ問題が発生した...。
sed 's/\(folder\)/[\1]/g' #正常に機能する sed 's/\(フォルダ\)/[\1]/g' #置き換えてくれない grep -i 'folder' #OK grep -i 'フォル' #OK grep -i 'フォルダ' #NG エラー発生
- そう、英語(半角英数)なら調子良く動いていたコマンドも、日本語を入力したとたん使い物にならなくなってしまった...。
- それにしても、「フォル」がOKで「フォルダ」はNG。ファイルによっては、英数字も日本語もすべてNGだったりする。
- いったい、何が原因でこのような現象になるのか?徹底的に調べてみた。
現象
- テキストエディット.appで、デスクトップに以下のようなファイル(folder.txt)を用意した。
applications folder documents folder アプリケーションフォルダ 書類フォルダ
- また、Finderでファイル名を選択してコピー、それをテキストエディットにペーストしたファイル(services.txt)を用意した。
照会_西濃運輸(カンガルー便).workflow 照会_〒EMS.workflow 照会_佐川急便.workflow 照会_JPエクスプレス(ペリカン便・日本通運).workflow 照会_〒翌朝10時郵便.workflow 照会_〒書留.workflow 照会_〒ゆうパック.workflow 照会_ヤマト運輸.workflow
ターミナル
$ cat ~/Desktop/folder.txt | sed s/folder/OOOOOO/g #OK $ cat ~/Desktop/folder.txt | sed s/フォルダ/OOOOOO/g #OK $ cat ~/Desktop/services.txt | sed s/workflow/OOOOOO/g #NG 異常出力 照会_ヤマト運輸.OOOOOOOOOO ン便・日本通運).OOOOOO $ cat ~/Desktop/services.txt | sed s/照会/OOOOOO/g #NG 異常出力 OOOOOO_ヤマト運輸.workflowowow 便・日本通運).workflow $ cat ~/Desktop/folder.txt | grep 'folder' #OK $ cat ~/Desktop/folder.txt | grep 'フォルダ' #OK $ cat ~/Desktop/services.txt | grep -i 'jp' #NG 異常出力 照会_ヤマト運輸.workflowowow 便・日本通運).workflow $ cat ~/Desktop/services.txt | grep -i '西濃' #NG 異常出力 照会_ヤマト運輸.workflowowow 便・日本通運).workflow $ cat ~/Desktop/services.txt | grep -i 'カンガルー' #NG 抽出なし $ cat ~/Desktop/services.txt | grep -i 'カン' #NG 異常出力 照会_ヤマト運輸.workflowowow 便・日本通運).workflow
Automator&「シェルスクリプトを実行」アクション
cat ~/Desktop/folder.txt | sed s/folder/OOOOOO/g #OK cat ~/Desktop/folder.txt | sed s/フォル/OOOOOO/g #OK cat ~/Desktop/folder.txt | sed s/フォルダ/OOOOOO/g #NG 置き換えなし cat ~/Desktop/services.txt | sed s/workflow/OOOOOO/g #OK cat ~/Desktop/services.txt | sed s/照会/OOOOOO/g #OK cat ~/Desktop/folder.txt | grep -i 'folder' #OK cat ~/Desktop/folder.txt | grep -i 'フォル' #OK cat ~/Desktop/folder.txt | grep -i 'フォルダ' #NG エラー発生 cat ~/Desktop/services.txt | grep -i 'jp' #NG 全部抽出 cat ~/Desktop/services.txt | grep -i '西濃' #NG 全部抽出 cat ~/Desktop/services.txt | grep -i 'カンガルー' #NG 全部抽出
AppleScript&do shell script
do shell script "cat ~/Desktop/folder.txt | sed s/folder/OOOOOO/g" --OK
do shell script "cat ~/Desktop/folder.txt | sed s/フォルダ/OOOOOO/g" --OK
do shell script "cat ~/Desktop/services.txt | sed s/workflow/OOOOOO/g" --OK
do shell script "cat ~/Desktop/services.txt | sed s/照会/OOOOOO/g" --OK
do shell script "cat ~/Desktop/folder.txt | grep -i 'folder'" --OK
do shell script "cat ~/Desktop/folder.txt | grep -i 'フォルダ'" --OK
do shell script "cat ~/Desktop/services.txt | grep -i 'jp'" --NG(全部抽出)
do shell script "cat ~/Desktop/services.txt | grep -i '西濃'" --NG(全部抽出)
do shell script "cat ~/Desktop/services.txt | grep -i 'カンガルー'" --NG(エラー発生)
do shell script "cat ~/Desktop/services.txt | grep -i 'カン'" --NG(全部抽出)
Automator&「AppleScriptを実行」アクション&do shell script
- AppleScript&do shell script と同じ結果
分析
データは揃った。以上の結果から、考えられる原因を想像してみた。
改行コード
- folder.txt(LF)とservices.txt(CR)の違いは、改行コードの違いだった。(CotEditorで開いて気付いた)
- fileコマンドで確認すると、以下のように表示される。
$ file ~/Desktop/folder.txt /Users/zari/Desktop/folder.txt: UTF-8 Unicode text $ file ~/Desktop/services.txt /Users/zari/Desktop/services.txt: UTF-8 Unicode text, with CR line terminators
- ターミナルでservices.txtがことごとく異常出力されてしまうのは、改行されずに同一行で上書き表示されてしまう現象が発生しているようだ。
- sedの置き換え自体は、うまく処理されていた。
- 但し、ターミナル画面では表示が重なってしまうため、異常な出力に見えてしまっていた。
- ファイルに出力して、CRを改行と見なしてくれるエディタで確認すると、問題なかった。
- grepの抽出では、テキスト全体が一行と認識されてしまうため、一つでもマッチすれば、テキスト全体が抽出される結果になってしまったようだ。
- おまけに、表示が重なってしまうため、異常な出力に見えてしまう。(sedと同様)
以下のページがたいへん参考になりました。感謝です!
テキストエンコーディング
改行コードがLFであるfolder.txtについては...
- Automatorの「シェルスクリプトを実行」アクションでのみ、「フォル」がOKで、「フォルダ」がNGという面白い結果になった。
- ターミナル、あるいはAppleScript&do shell script では正常に処理される。
おそらく、文字コードに由来する原因だと思うが、どうして上記のような現象になるのかは、分からなかった。
- fileコマンドで調べた限り、同じUTF-8であるはずなのに...。
ところで、改行コードがCRであるservices.txtについて、CRに問題があることが分かっているので、LFに変換してやり直してみた。
- ターミナル
$ cat ~/Desktop/services.txt | /opt/local/bin/nkf -w -Lu | grep -i 'カンガルー' #NG 抽出なし $ cat ~/Desktop/services.txt | /opt/local/bin/nkf -w -Lu | grep -i 'カン' #OK 照会_西濃運輸(カンガルー便).workflow 照会_JPエクスプレス(ペリカン便・日本通運).workflow
- AppleScript&do shell script
do shell script "cat ~/Desktop/services.txt | /opt/local/bin/nkf -w -Lu | grep -i 'カンガルー'" --NG(エラー)
do shell script "cat ~/Desktop/services.txt | /opt/local/bin/nkf -w -Lu | grep -i 'カン'" --OK
--結果↓
--"照会_西濃運輸(カンガルー便).workflow
--照会_JPエクスプレス(ペリカン便・日本通運).workflow"
cat ~/Desktop/services.txt | /opt/local/bin/nkf -w -Lu | grep -i 'カンガルー' #OK cat ~/Desktop/services.txt | /opt/local/bin/nkf -w -Lu | grep -i 'カン' #OK
つまり、改行コードがCRであるservices.txtについては...
- Automatorの「シェルスクリプトを実行」アクションでのみ、正常に処理される。
- ターミナル、あるいはAppleScript&do shell script では、「カン」はOKで、「カンガルー」はNGというfolder.txtとは全く逆の結果になった。
さらに面白いことに...
- 最終行に自分自身が手入力した「照会_西濃運輸(カンガルー便).workflow2」を追記してみた。
- すると、今まで抽出なしだったターミナルでの grep -i 'カンガルー' で、抽出されたしまったのである!
$ cat ~/Desktop/services.txt | /opt/local/bin/nkf -w -Lu | grep -i 'カンガルー' 照会_西濃運輸(カンガルー便).workflow2
同じ「カンガルー」のはずなのに、ファイル名のコピーと、手入力に一体、何の違いがあるというのだ?こうなったら最後の手段、HexEditorを使って、バイトコードの並びを比較してみることにした。
# (カンガルー便) の部分のバイトコード ef bc 88 e3 82 ab e3 83 b3 e3 82 ab e3 82 99 e3 83 ab e3 83 bc e4 be bf ef bc 89 #ファイル名をコピーしたもの ef bc 88 e3 82 ab e3 83 b3 e3 82 ac e3 83 ab e3 83 bc e4 be bf ef bc 89 #手入力したもの
なんと!ファイル名をコピーした方は3バイト分余分なコードが付加されていたのだ!
コード修正
理由が分かれば、方針を決めて、修正するだけである。
- 改行コードはLF(UNIX形式)に統一する。
cat '入力ファイル' | nkf -Lu
- 文字コードは普通のUTF-8、あるいはUTF-8-MACのどちらかに統一する。
- AutomatorならTF-8-MACに統一した方が良さそう。
- ターミナル、AppleScript&do shell scriptならUTF-8に統一した方が良さそう。
cat '入力ファイル' | iconv -f UTF-8-MAC -t UTF-8 # あるいは cat '入力ファイル' | iconv -f UTF-8 -t UTF-8-MAC
/opt/local/bin/nkf -w -Lu|iconv -f UTF-8 -t UTF-8-MAC
- ターミナルやAppleScript&do shell script なら、パイプを挟んでこんな感じ。
$ cat ~/Desktop/folder.txt | /opt/local/bin/nkf -w -Lu | iconv -f UTF-8-MAC -t UTF-8 | sed 's/\(フォルダ\)/【\1】/g' applications folder documents folder アプリケーション【フォルダ】 書類【フォルダ】 $ cat ~/Desktop/folder.txt | /opt/local/bin/nkf -w -Lu | iconv -f UTF-8-MAC -t UTF-8 | grep -i 'フォルダ' アプリケーションフォルダ 書類フォルダ $ cat ~/Desktop/services.txt | /opt/local/bin/nkf -w -Lu | iconv -f UTF-8-MAC -t UTF-8 | sed 's/\(カンガルー\)/【\1】/g' 照会_西濃運輸(【カンガルー】便).workflow 照会_〒EMS.workflow 照会_佐川急便.workflow 照会_JPエクスプレス(ペリカン便・日本通運).workflow 照会_〒翌朝10時郵便.workflow 照会_〒書留.workflow 照会_〒ゆうパック.workflow 照会_ヤマト運輸.workflow 照会_西濃運輸(【カンガルー】便).workflow2 $ cat ~/Desktop/services.txt | /opt/local/bin/nkf -w -Lu | iconv -f UTF-8-MAC -t UTF-8 | grep -i 'カンガルー' 照会_西濃運輸(カンガルー便).workflow 照会_西濃運輸(カンガルー便).workflow2
以上で、sed・grepで濁点と改行をまともに扱えるようになった!