【 awk 】コマンド(応用編その3)――テキストの加工とパターン処理、配列の活用Linux基本コマンドTips(209)

本連載は、Linuxのコマンドについて、基本書式からオプション、具体的な実行例までを紹介していきます。今回は、テキストのパターン処理を行う「awk」コマンドです。

» 2018年05月24日 05時00分 公開
[西村めぐみ@IT]
「Linux基本コマンドTips」のインデックス

Linux基本コマンドTips一覧

 本連載は、Linuxのコマンドについて、基本書式からオプション、具体的な実行例までを紹介していきます。今回はテキストのパターン処理を行う「awk(gawk)」コマンドです。

 連載第115回第116回第117回第118回第119回第120回に続き、awkの応用を説明します。

awk(オーク)コマンドとは?

 「awk」は空白などで区切られたテキストを処理するコマンドです。演算機能もあり、プログラミング言語としても使用されています。

 Linux環境で使用されているのは、GNUプロジェクトによる「gawk」コマンドが多く、例えばCentOS 7の場合、awkは/usr/bin/gawkへのシンボリックリンクとなっています。

 Ubuntu 15では、Michael D. Brennan氏による「mawk」が収録されています(awkは/etc/alternatives/awkへの、/etc/alternatives/awkは/usr/bin/mawkへのシンボリックリンク)。

 どちらも、もともとのawkに加えてPOSIX 1003.2への準拠や組み込み変数、正規表現指定のバリエーションなどが拡張されています。



awkコマンドの書式

awk [オプション] [コマンド] [ファイル……]

※[ ]は省略可能な引数を示しています





awkの主なオプション

短いオプション 意味
-f ファイル名 awkスクリプトが書かれたファイルを指定する
-F 区切り文字 区切り文字を指定する(デフォルトは空白文字)
-v 変数名=値 変数を定義する

※ gawk(GNU版awk)の場合、長いオプションも使用可能。-fは--file program-file、-Fは--field-separator、-vは--assign。gawkにはこの他にも多数のオプションがある。



awkで使用できる主な組み込み変数

変数名 意味
ARGC 引数の個数
ARGV 引数(配列)
ENVIRON 環境変数を収めた連想配列。例えば環境変数LANGならばENVIRON["LANG"]と参照できる
FILENAME 現在処理しているファイルの名前
FNR 現在処理しているファイルのレコード番号(処理しているファイルが1つの場合はNRと同じ値になる)
FS フィールドの区切り文字(-Fオプションで変更可能、デフォルトはスペース)
NR 現在処理しているレコード番号(行番号)
OFS 出力時のフィールドの区切り(デフォルトは空白)
ORS 出力時のレコードの区切り(デフォルトは改行)
RS レコードの区切り(デフォルトは改行)

awkで使用できる主な文字列操作用の関数

関数(引数) 意味
gsub(r, s, t) 「文字列t」中で「正規表現r」にマッチした箇所全てをsに置き換えて、置き換えた個数を返す。tを指定しなかった場合は「$0」(読み込んだ行全体)が対象
index(s, t) 「文字列s」に含まれる文字列tの位置を返す。tが含まれていない場合は0を返す
length(s) 「文字列s」の長さを返す。sを指定しなかった場合には「$0」の長さを返す
match(s, r) 「文字列s」で「正規表現r」にマッチする位置を返す。その際、内部変数のRSTARTに開始位置、RLENGTHに長さをセットする。マッチしない場合は0を返す
split(s, a, r) 「文字列s」を「正規表現r」で分割し、配列aに格納する。rを省略した時は「FS」(区切り文字、デフォルトは空白)となる
sprintf(フォーマット指定, 変数リスト) フォーマット指定に従って整形した文字列を返す。指定方法は「printf」(第118回)と共通
sub(r, s, t) 「文字列t」の中で「正規表現r」へ最初にマッチした箇所をsに置き換える。tを指定しなかった場合は「$0」が対象
substr(s, i, n) 「文字列s」のi文字目からn文字分を返す。nを省略した場合はi文字目以降全てを返す
tolower(s) 「文字列s」のうち大文字を全て小文字に変換したものを返す
toupper(s) 「文字列s」のうち小文字を全て大文字に変換したものを返す

※ この他、sin()やlog()、rand()などの数値関数、さらにgawkの場合はsystime()やmktime()などの時間関数を利用できる。




配列を使って文字列の出現回数を数える

 awkコマンドは連想配列と呼ばれる配列を内蔵しています。連想配列を使いこなすことで、さまざまなデータ処理を容易に処理できます。

 連想配列は「配列名[名前]」のように使用します。配列の「添え字」に数値以外の文字列を利用できることが特徴です。

 例えば画面1の上半分に示したデータ(sample1.txt)があったとき、各都道府県が何回登場したのかを調べたいとしましょう。この場合、以下のようなスクリプトファイル(sample1.awk)を使って処理できます。

{	#全ての行に対して行う処理
  count[$1]++	#count[都道府県名]を1増やす
}
END{	#全ての行を読み終わった後で行う処理
  for(name in count){
    print name,count[name]
  }
}

 このスクリプトは大きく2つのブロック({……})からできています。最初のブロックでは、countという配列を使い、東京と書いてある行を読み込んだら、count["東京"]を1増やす、神奈川を読み込んだらcount["神奈川"]を1増やす……という処理で、各都道府県の出現回数を数えています。

 countの添え字を表す「$1」には各行の1つ目のフィールドが入ります。このため、それぞれの都道府県を「count[$1]++」で数えることができます(※1)。なお「++」は1増やすという意味の演算子です(第119回)。

※1 sample1.txtの場合は、1行に1フィールドしかないため、読み込んだ行全体を表す「$0」を使って、「count[$0]++」としても結果は同じになる。



 続くENDブロックでは、for文を使ってcount配列の内容を全て出力しています(第117回)。for文で配列を参照する場合は「for (変数名 in 配列名) 処理」のように指定します。

 ここでは、nameという変数に、count配列に保存されている都道府県名をセットし、count[name]で配列の値を出力しています。

 画面1の下半分では、sample1.awkと同じ内容をコマンドラインで直接記述しました。記述内容は「awk '{count[$1]++}END{for(name in count)print name,count[name]}'」です(※2)。

※2 for文で実行しているのは、print文1つだけなので{}を省略した。



コマンド実行例

cat ファイル名 | awk '処理内容'

(ファイルの内容をawkコマンドに送り、処理を進める)

cat ファイル名 | awk -f awkファイル名

(ファイルの内容をawkコマンドに送る。処理内容はawkファイルに書かれている)


画面1 画面1 集計データと集計結果 都道府県名が何回登場したのかを集計した。


配列を使って集計する

 先ほどと同じ要領で、都道府県名と数値からなるデータを集計してみましょう。画面2の上半分にあるデータ(sample2.txt)が対象です。今回は次のようなスクリプトファイル(sample2.awk)を使います。画面2の下半分では、sample2.awkと同じ内容をコマンドラインで直接記述しました(※3)。

※3 コマンドラインの指定内容は以下の通り。「awk '{count[$1]++;sum[$1]+=$2}END{for(name in count)print name,count[name],sum[name]}'」



{
  count[$1]++	#count[都道府県名]を1増やす
  sum[$1]+=$2	#sum[都道府県名]に2番目のフィールドの値を加算する
}
END{
  for(name in count){
    print name,count[name],sum[name]
  }
}
画面2 画面2 集計データを集計した結果 都道府県名の登場回数と、都道府県ごとの数値をそれぞれ集計した。


配列から不要な要素を削除する

 画面1や画面2にはデータ以外のテキストが含まれていませんでした。しかし、画面3の上部に示したように、見出しが入っているテキストでは、この部分を取り除く処理が必要です。こうしないと、画面3の中央にあるように集計データに「地名 1 0」という無意味な結果が混ざってしまいます。

 awkコマンドが備えるdelete文を使うことで、配列から不要な要素を削除できます。画面3では、見出しを「count["地名"]」としてカウントしているので、「delete count["地名"]」で削除します(※4)。

 画面3の下部では、sample3.awkと同じ内容をコマンドラインで直接記述しました(※5)。

※4 配列sumも「sum["地名"]」でゼロと集計されている。今回のケースでは、for文で配列countを使って出力する要素を決めているため「delete sum["地名"]」の処理は省略した。
※5 コマンドラインの指定内容は以下の通り。「awk '{count[$1]++;sum[$1]+=$2}END{delete count["地名"];for(name in count)print name,count[name],sum[name]}'」



{
  count[$1]++
  sum[$1]+=$2
}
END{
  delete count["地名"]	#count["地名"]を削除する
  for(name in count){
    print name,count[name],sum[name]
  }
}
画面3 画面3 必要ない行を取り除いて集計したところ 「地名 値」という不要な行を除外した。

 なお、delete文を使わず、集計時に先頭行を対象外にすることもできます。最初の処理の頭に「(NR>1)」を付けて「(NR>1){count[$1]++;sum[$1]+=$2}」のようにするとよいでしょう。これは2行目以降を処理の対象とするという意味です。

(NR>1){	#2行目以降を対象とする
  count[$1]++
  sum[$1]+=$2
}
END{
  for(name in count){
    print name,count[name],sum[name]
  }
}

 集計後に配列から不要な要素を削除するのではなく、集計前に見出しに含まれる文字列(今回のケースでは"地名")を含む行を対象外とすることもできます。「!/地名/{count[$1]++;sum[$1]+=$2}」のようにします。

!/地名/{	#"地名"を含まない行を対象とする
  count[$1]++
  sum[$1]+=$2
}
END{
  for(name in count){
    print name,count[name],sum[name]
  }
}


筆者紹介

西村 めぐみ(にしむら めぐみ)

PC-9801NからのDOSユーザー。PC-486DX時代にDOS版UNIX-like toolsを経てLinuxへ。1992年より生産管理のパッケージソフトウェアの開発およびサポート業務を担当。著書に『図解でわかるLinux』『らぶらぶLinuxシリーズ』『Accessではじめるデータベース超入門[改訂2版]』『macOSコマンド入門』など。2011年より、地方自治体の在宅就業支援事業にてPC基礎およびMicrosoft Office関連の教材作成およびeラーニング指導を担当。


Copyright © ITmedia, Inc. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。