【 awk 】コマンド(応用編その4)――テキストの加工とパターン処理、split関数の活用Linux基本コマンドTips(210)

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

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

Linux基本コマンドTips一覧

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

 連載第115回第116回第117回第118回第119回第120回第209回に続き、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()などの時間関数を利用できる。




split関数で文字列を分割する

 awkを使った処理では、文字列を特定の文字ごとに分割できます。awkが備えるsplit関数を使い、例えば「:」で区切られている文字列であれば「split(元の文字列, 配列, ":")」のように指定します。

 分割した結果が幾つになったか、調べることもできます。「変数=split(元の文字列, 配列, ":")」のように書きます。

 例えば、環境変数PATHの内容は「ディレクトリ名:ディレクトリ名:ディレクトリ名……」のように、ディレクトリ名が「:」記号で区切られています。これを以下のpath.awkのように処理することで、各ディレクトリを1行ずつ分けて表示できます。

{
  cnt=split($0, path, ":")
  for (i=1; i<=cnt; i++) print path[i]}
}
path.awk

 画面1では、「echo $PATH」で環境変数PATHを出力し、その結果を「awk '{cnt=split($0,path,":");for (i=1;i<=cnt;i++)print path[i]}'」で切り分けています(※1)。処理内容はpath.awkと同じです。ただし、コマンドラインで入力しやすいよう、改行や空白文字を省略しています。

※1 awkスクリプトの中で環境変数を取得する場合は、ENVIRON["PATH"]のように書く。awkは何らかの入力を受け取って(あるいはファイルを読み込んで)処理するコマンドであるため、画面1ではechoコマンドを使い、その結果をawkが受け取るという形で処理している。



echo $PATH
echo $PATH | awk '{cnt=split($0,path,":");for (i=1;i<=cnt;i++)print path[i]}'
コマンドラインの内容
画面1 画面1 環境変数PATHの内容をディレクトリごとに切り分けて表示したところ

 区切り文字は1文字でなくても構いません。

 例えば、画面2の最初のコマンドラインでは、「ls -l」の実行結果をgrepコマンド(第10回)で絞り込み、シンボリックリンクのみを取り出した後、cutコマンド(第60回)で47バイト目以降、つまりファイル名部分だけを出力しています。

 出力後のシンボリックリンクの形は「ファイル名 -> リンク先」となっています。そこで画面2の2番目のコマンドラインでは、シンボリックリンクに対して「split($0,ary," -> ")」のように指定することで、ファイル名とリンク先を「 -> 」で分割しています。読み込んだ行全体($0)を、「" -> "」で区切って配列aryを作る、という意味です。

ls -l / | grep ^l | cut -b 47-
ls -l / | grep ^l | cut -b 47- | awk '{split($0,ary," -> ");print ary[2]}'
コマンドラインの内容
画面2 画面2 lsコマンドの出力からシンボリックリンクのリンク先を取り出したところ


CSV形式のデータをsplit関数で分割する

 split関数は、処理対象となるデータが空白文字ではなく「,」記号で区切られたCSV形式の場合でも対応できます。

 ただしCSVの場合は、FS=または-Fオプションで区切り文字を指定した方が扱いやすいでしょう(第120回)。

 画面3は、第209回のsample2.awkと同じ処理を、csvデータに対して試みた例です。画面3の上半分でデータ(sample4.csv)を表示し、画面3の下半分では、sample3.awkと同じ内容をコマンドラインで直接記述しました(※2)。

 処理内容のうち、BEGIN{}のブロックで、読み込みの区切り文字と出力の区切り文字を共に「,」に変えています。

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



BEGIN{
  FS=","	#読み込みの区切り文字を「,」に変える
  OFS=","	#出力の区切り文字を「,」に変える
}
{
  count[$1]++	#count[都道府県名]を1増やす
  sum[$1]+=$2	#sum[都道府県名]に2番目のフィールドの値を加算する
}
END{
  for(name in count){
    print name,count[name],sum[name]
  }
}
sample3.awk
画面3 画面3 CSV形式のデータを集計したところ split関数を利用した。


フィールドへsplit関数を当てはめる

 画面3より複雑なCSVデータであっても、awkコマンドが役立つ場合があります。

 例えば図4の上半分に示したsample5.csvでは、CSVデータの中に、「値=8」のような「=」区切りのフィールドがあります。

 このような場合、「split($2,ary,"=")」のように、フィールドに対してsplit関数を施すことで、欲しい値を簡単に取り出すことができます。

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

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



BEGIN{
  FS=","
  OFS=","
}
{
  count[$1]++
  split($2,ary,"=")	#2番目のフィールドを"="で分割して配列aryにセット
  sum[$1]+=ary[2]	#aryの2つ目のフィールドを加算
}
END{
  for(name in count){
    print name,count[name],sum[name]
  }
}
sample4.awk
画面4 画面4 CSV形式のデータを集計したところ split関数を利用した。


筆者紹介

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

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のメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。