rcmdnk's blog

sed & awk (Nutshell Handbooks) 電子書籍: Arnold Robbins, Dale Dougherty

sedでタブ文字をスペースとかに変更しようとした時、 Linuxで使っていたスクリプトがMacで使えなかったので Macのsed(BSD sed)でのタブ文字の使い方についてちょっと調べてみました。

BSD sedでタブ文字を使う

Linux版(GNU版)sedでは\tがタブ文字として使えて、これをそのまま

$ printf "XXX\tXXX\n" | sed "s/\t/aaa/g"
XXXaaaXXX

の様にすればタブ部分がXXXに変わります。

これをMacやBSDなOSでやってみると

$ printf "XXX\tXXX\n" | sed "s/\t/aaa/g"
XXX	XXX

こんな風にタブ部分がそのまま残ってしまいます。 ここにもGNU/BSDの違いが。

BSDなsedでタブ文字を変換する方法はいくつかありますが、 一つは Ctrl-V TabCtrl-Vを使ったメタ文字直接入力を使って

$ printf "XXX\tXXX\n" | sed "s/	/aaa/g"

と書く方法。上のs/の後の空白は単なるスペースではなく、 Ctrl-V Tabで入力したものです。 (上のコマンドをそのままコピペするとスペースになってしまうので 試すと時は自分でCtrl-Vを入力する必要があります。)

次にprintfを使ってタブ文字を出してあげる方法。

$ printf "XXX\tXXX\n" | sed "s/$(printf "\t")/aaa/g"

例の最初のところで\tをタブ文字として出力してますが、 後ろの変換のところでもこれを使ってタブ文字を出して変換させています。

もう一つはANSI-Cクォーティングを使う方法。

$ printf "XXX\tXXX\n" | sed s/$'\t'/"aaa"/g

単に\t等のメタ文字を使おうとするとそれぞれのコマンド自身で解析するため、 対応してないメタ文字は使えませんが、 $'\t'の様にANSI-C quotingを使うとシステムの方でまずCtrl-V と同様にタブ文字として認識してコマンドに渡すので 上手く行く、ということです。(多分。)

printf\tを理解しますが、echoは通常メタ文字を理解しません。 (-eオプションで理解する様になります。) 一方で$'\t'であれば通常のechoでもタブ文字として認識します。

BSD sedのmanを見ると\n(改行)は改行とマッチする、とありますが(Sed Regular Expressionsの所) これだけ書いてあって他には無いのでメタ文字としてはこれだけが使えるみたいです。 また、\nも最初にマッチさせる方には使えますが 出力側として使うことは出来ません。 (GNUは出力としても使えます。)

ちなみに、ANSI-C quotingをさらに"'でクォートしてしまうと ANSI-C quotingとは解釈されなくなるのでsedのコマンド全体を囲うこことは避けないといけません。 この場合、スペースを含む文字を使いたい場合にエラーが出るので、 上の様に("aaa")必要な部分だけをクォートする必要があります。

これらの3つの方法はもちろんGNU sedでも使えます。

また、上の例はタブ文字を変換する、でしたが、 タブ文字へ変換する、も同様に上の3つの方法でタブ文字を出せば出来ます。

方法としては最初のものはここでも分かる通りパット見良くわからないのと コピペ出来ないことがあるのでなるべく避けたほうが良いと思います。

最後のANSI-C quotingのものが一番短く書けるのでいいかな、と思いますが、 全体をクォートできないので場合によっては printfを使った方がすっきりするかもしれません。

その他のタブ文字変換方法

sedは色々便利ですが、単にタブをスペースに返還したいだけ、とかであれば他の方法も。

まずは文字変換のtr:

$ printf "XXX\tXXX\n" | tr "\t" " "

trにもGNUとBSDで微妙に違いがありますが、 manを見てみると、両方共に\tをタブとして理解する、という記述が見れ、 上の方法は両方でゆう宇高です。

この方法は一文字への変換なので複数スペースにしたかったり 複数文字へ変換したい場合は他の方法にする必要があります。

ターミナル出力と同じ様なスペース具合にしたい場合には expandというコマンドを使う方法があります。

$ printf "\tXXX\tXXX\n" | expand
        XXX     XXX$

expandはタブ文字を展開するためのコマンドですが、 デフォルトでは ターミナルに直接タブ文字を出した時と同じ表記になります。 大概の場合、上の様に行頭に来るタブは8文字、その後にあるタブは4文字で展開される様な形。 (タブ間の文字数や全体の文字数で感覚が狭くなったりもします。)

このコマンドもBSD版があって多少オプションが違ったりしますが 簡単な使い方は一緒です。 ターミナル表示と同じになるので、 タブ文字を使って出力しているログ等をどこかにコピペする時には これを使ってタブを変換してから貼り付けると綺麗に表示することが出来ます。

他にはsed以外の高等なコマンドを使う方法として、 awkを使う事も出来ます。

$ printf "XXX\tXXX" | awk '{gsub("\t", "aaa");print}'

awkはメタ文字をそのまま理解してくれる様です。 また、awkはGNUのものではないので LinuxでもBSD(Mac)でもバージョンが同じであれば同じ機能です。 (gawkといったGNU拡張版もありますが。)

後はもうGNU版のsedを入れてしまう方法。 MacでHomebrewなら

$ brew install gnu-sed

でGNU版のsedがgsedという名前で入ります。 もし、sedという名前でこれを代わりに使いたければ

$ brew install gnu-sed --with-default-names

というオプションを付けるとgsedではなくsedという名前で /usr/local/bin等にインストールされます。

まとめ

シェルスクリプトを作る際にsedコマンドは複雑な処理をしようとする際には 便利なのでよく使いますが、 意外とGNU版、BSD版で違いが大きいので注意が必要だ、ということ。

Sponsored Links
Sponsored Links

« Vim 8.0リリース Facebookのシェア数の獲得方法のアップデート »

}