本連載は、Linuxのコマンドについて、基本書式からオプション、具体的な実行例までを紹介していきます。今回は、テキストのパターン処理を行う「awk」コマンドです。
本連載は、Linuxのコマンドについて、基本書式からオプション、具体的な実行例までを紹介していきます。今回はテキストのパターン処理を行う「awk(gawk)」コマンドです。
連載第115回、第116回、第117回、第118回、第119回、第120回、第209回に続き、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 [オプション] [コマンド] [ファイル……]
※[ ]は省略可能な引数を示しています
短いオプション | 意味 |
---|---|
-f ファイル名 | awkスクリプトが書かれたファイルを指定する |
-F 区切り文字 | 区切り文字を指定する(デフォルトは空白文字) |
-v 変数名=値 | 変数を定義する |
※ gawk(GNU版awk)の場合、長いオプションも使用可能。-fは--file program-file、-Fは--field-separator、-vは--assign。gawkにはこの他にも多数のオプションがある。
変数名 | 意味 |
---|---|
ARGC | 引数の個数 |
ARGV | 引数(配列) |
ENVIRON | 環境変数を収めた連想配列。例えば環境変数LANGならばENVIRON["LANG"]と参照できる |
FILENAME | 現在処理しているファイルの名前 |
FNR | 現在処理しているファイルのレコード番号(処理しているファイルが1つの場合はNRと同じ値になる) |
FS | フィールドの区切り文字(-Fオプションで変更可能、デフォルトはスペース) |
NR | 現在処理しているレコード番号(行番号) |
OFS | 出力時のフィールドの区切り(デフォルトは空白) |
ORS | 出力時のレコードの区切り(デフォルトは改行) |
RS | レコードの区切り(デフォルトは改行) |
関数(引数) | 意味 |
---|---|
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を使った処理では、文字列を特定の文字ごとに分割できます。awkが備えるsplit関数を使い、例えば「:」で区切られている文字列であれば「split(元の文字列, 配列, ":")」のように指定します。
分割した結果が幾つになったか、調べることもできます。「変数=split(元の文字列, 配列, ":")」のように書きます。
例えば、環境変数PATHの内容は「ディレクトリ名:ディレクトリ名:ディレクトリ名……」のように、ディレクトリ名が「:」記号で区切られています。これを以下のpath.awkのように処理することで、各ディレクトリを1行ずつ分けて表示できます。
{ cnt=split($0, path, ":") for (i=1; i<=cnt; i++) print path[i]} }
画面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文字でなくても構いません。
例えば、画面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]}'
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] } }
画面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] } }
西村 めぐみ(にしむら めぐみ)
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.