【訂正】「文字列の置換」節の2つ目、3つ目のサンプルコードに誤りがあり、訂正いたしました。
2つ目:[誤]gsub(/./, "A"); ""' ⇒ [正]gsub(/./, "A")'
3つ目:[誤]gsub(/./, "A"); ""' ⇒ [正]gsub(/./, "A") ""'
この誤りはCodeZineに転載する際に混入したものです。読者の皆様、著者の斉藤様、USP Magazine編集部様にご迷惑をおかけしましたことを、深くお詫びいたします。
文字列の置換
AWKには、正規表現にマッチした文字列を置換する関数が用意されています。最初にマッチした文字列だけを置換するのがsub関数で、マッチした全ての文字列を置換するのがgsub関数です。
$ echo 'abcde' | awk '{sub(/./, "A"); print $0}'
Abcde
$ echo 'abcde' | awk '{gsub(/./, "A"); print $0}'
AAAAA
上の例では正規表現"."(つまり1 文字)を文字列"A"に置換しているのですが、sub関数の場合には最初の文字だけ、gsub関数の場合にはすべてが置き換わっていることが分かります。
sub関数とgsub関数が取る引数は、最初に置換対象になる正規表現、次に置換後の文字列、3番目の引数は置換対象文字列です。ただし、3番目の引数は省略可能であり、省略した場合には$0が用いられます。
さて、sub関数、gsub関数ともに戻り値は「置換に成功した個数」であることに注意してください。よく置換後の文字列だと勘違いをして、以下のように省略する人がいます。
$ echo 'abcde' | awk '$0 = gsub(/./, "A")' 5
もし省略するのであれば、以下のように記述するべきでしょう。
$ echo 'abcde' | awk 'gsub(/./, "A") ""' AAAAA
この記述は特殊ですが、sub関数やgsub関数に空文字列を繋げています。こうすることで、sub関数やgsub関数が0(ゼロ)を返しても表示することができます。これはAWKが数値の0は偽とするのに対して、文字列の0は真とすることを利用したものです。GNU拡張では置換後の文字列を返すgensub関数が加わり、正規表現の拡張や、後方参照による一部のパターンのみの置換や抜き出しも可能になっています。興味がある人は調べてみてください。
文字列の連接
今まで特に断りなく書いてきていますが、AWKで文字列を繋げる(これを「連接」と言います)場合には、文字列をスペースで繋げます。実際には明確に区別ができればスペースすら不要です。
$ echo 'abcde' | awk '{print $0 "fghij"}'
abcdefghij
これは他の言語と比べると特殊であり、演算子がない状態であっても文字列が並ぶと「連接」として扱われます。また、AWKにはシェルの変数のように最初に"$"が付くと変数を表すといった接頭文字がありませんので、以下のようにprint文を間違って"prin"と記述してしまったAWKスクリプトはエラーになりません。
$ echo 'abcde' | awk '{prin $0 "fghij"}'
←何も表示されない
AWKの中では、変数prinと$0と文字列"fghij"が連接されているだけだと判断されます。また、表示するべき命令が記載されていませんので、エラーもなく、何も表示されません。
別の方法として、他の言語でも多く実装されているsprintf関数があります。sprintf関数は他の言語と同じように用いることができ、文字列だけでなく、数値の場合にはフォーマット指定を行うことができます。
$ echo 'abcde' | awk '{print sprintf("%s%s", $0, "fghij")}'
abcdefghij
もちろん、AWKにはprintf文(sprintfは関数ですが、printfは文です)も準備されていますので、以下のように記述できます。
$ echo 'abcde' | awk '{printf("%s%s\n", $0, "fghij")}'
abcdefghij
以前は、連接とsprintf関数ではsprintf関数の方が高速であると言われていましたが、今のGNU AWKではほぼ同じ速度で動作します。したがって、どちらを使うかはユーザーが判断して見やすい方を使えば良いでしょう。
さて、printf文もsprintf関数も同じですが、これらの文や関数には数値を四捨五入したり切り捨てたりする機能はありません。ユーザーがビルドした環境に依存するということを覚えておいてください。よくprintf文やsprintf関数で四捨五入をしたり切り捨てたりする方法を目にしますが、少なくともAWKでできるのはGNU拡張で丸める手法を定義できるようになってからです。
大文字小文字変換
AWKには標準で大文字から小文字、またはその逆を行う関数が用意されています。
$ echo 'abcde' | awk '{print toupper($0)}'
ABCDE
$ echo 'ABCDE' | awk '{print tolower($0)}'
abcde
変換すること自体を目的として使うよりも、大文字小文字を同一視させたい場合、かつ古いAWKで組込変数IGNORECASEが有効ではない場合に使われることが多いでしょう。
$ echo 'Awk' | awk 'tolower($0) == "awk"' Awk
このようにすることで、大文字と小文字を無視した条件分岐を行うことができます。
AWKの区切り?
少し紙面が余ったので、閑話休題ということで、AWK界隈のニュースをお知らせします。POSIXではAWKの区切りとして";;"(2回のセミコロン)となっているそうです。
$ seq 1 10 | nawk '/1/ ;; /2/' 1 2 10
ところが GNU AWKではこれに準拠していないということが最近話題になりました。
$ seq 1 10 | gawk '/1/ ;; /2/' gawk: cmd. line:1: each rule must have a pattern or an action part
確かにエラーになります。GNU AWKのメンテナーのArnold Robbinsは、POSIXの記載自体が古い上、for文のように区切りが全て";;"になっているわけではないので、AWKの作者のBrian Kernighan(C言語の生みの親である"K&R"の"K"の人で、AWKの"K"の人ですね)に聞いてみようじゃないかと提案します。
Kernighan大先生からのメール注1を超意訳すると「いや~、すまんかった。実はアルフレッド(AWKの"A" の人)もピーター(AWKの"W"の人)もそこまでAWKを書きこなしていたわけじゃなかったんじゃよ。1988年にPOSIXを修正しようかと思っておったんじゃが、そのままにしていたら今日また同じことになったんで、こりゃ決めんといかんな。アーノルドの言うとおりに1つのセミコロンに決定じゃ」という感じの緩めのメールなのですが、あのKernighan大先生から全世界のAWKユーザーにメールされたというのはかなりインパクトがあったと思います。
したがって以下の文法が適用されます。これはnawk、gawk問わずに動作します。
$ seq 1 10 | awk '/1/ ;; /2/' 1 2 10
めでたし、めでたし。
注1:Re: [bug-gawk] use of ;; as terminator, request for grammar help
『USP MAGAZINE』は日本で唯一のシェルスクリプト総合誌(毎月25日発売)。最新号 2014 August(Vol.16)の巻頭特集は「IT美女特集」! ITの世界で活き活きと働く彼女たちの魅力に迫ります。そして、人気コーナー「IT美女図鑑」も復活! データセンターをこよなく愛する女性にお話をうかがいました。後藤大地さんの「漢のUNIX」は今回、フラグメントサイズの変更と性能への影響について検証。文末の太鼓をたたく後藤さんの雄姿も必見です。濱田康貴さんの「教えて先輩♡サーバー運用お助けTips」では、バックアップについての解説。備えあれば憂い無しです! もちろん、好評連載「ユニケージ開発手法コードレビュー」と、AWK処方箋の第5回なども掲載です!
► お求めは、Amazon.co.jp、USP研究所のWebサイト、取り扱い書店まで。
USP MAGAZINE 2014 August(Vol.16)
価格:500円+税
仕様:B5版 64ページ オールカラー
発行人:ユニバーサル・シェル・プログラミング研究所
