【 awk 】コマンド(応用編その6)――テキストの加工とパターン処理、複数ファイルの処理:Linux基本コマンドTips(212)
本連載は、Linuxのコマンドについて、基本書式からオプション、具体的な実行例までを紹介していきます。今回は、テキストのパターン処理を行う「awk」コマンドです。
本連載は、Linuxのコマンドについて、基本書式からオプション、具体的な実行例までを紹介していきます。今回はテキストのパターン処理を行う「awk(gawk)」コマンドです。
連載第115回、第116回、第117回、第118回、第119回、第120回、第209回、第210回、第211回に続き、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オプションで変更可能、デフォルトはスペース) |
NF | 現在処理しているレコードのフィールド数 |
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つのファイルだけでなく、複数のファイルをまとめて処理できます。「awk -f スクリプトファイル *.txt」あるいは「awk 'コマンド' *.txt」のようにファイル名を指定します。
処理中のファイルの名前をawkスクリプト内部から参照するには、変数FILENAMEを使います。
画面1のawkコマンドでは「*.txt」を読み出し、ファイル名と1行目の内容を出力しています。if文(第120回)で使用している変数FNRは、各ファイル中のレコード番号を表しています。
比較のため、全ファイルを通した行数を保存しているNRの値も一緒に出力しました。
コマンド実行例
awk '{if (FNR==1){print FILENAME, FNR, NR, $0}}' *.txt
(複数ファイルに対して、ファイル名と1行目の内容などを出力する)
複数のファイルを処理した場合のBEGIN、ENDの扱い
awkスクリプトで用いる「BEGIN」と「END」(第117回)には、対象となるデータの処理を開始する前と後に実行したい内容をそれぞれ指定します。
ファイルが複数ある場合は、ファイルごとにBEGINとENDを実行するのではなく、全ファイルの処理を始める前と終わった後に実行します。
以下のスクリプト(head3.awk)は、ファイル名と各ファイルの先頭3行を行番号付きで出力します。headコマンド(第3回)同様、ファイルとファイルの間には空行を入れるようにしました。
BEGINとENDの動作を確認するために、BEGINブロックでは「BEGIN」と出力し、ENDブロックで「END」と出力しています。
BEGIN { print "BEGIN" #「BEGIN」と出力 } { if (FNR == 1){ #ファイルの先頭行の場合 if (NR != 1){ #トータル行数が1ではない場合(2つ目以降のファイル) print "" #空行を出力 } print "==>",FILENAME, "<==" #「 ==> ファイル名 <==」を出力 } if (FNR <= 3){ #3行目以内の場合 print FNR,NR,$0 #行番号、トータルの行番号、行の内容を出力 } } END { print "END" #「END」と出力 }
ファイルに出力する
awkのprint文では、コマンドラインと同じようにリダイレクトを利用できます。例えば、「{print $0 > "ファイル名"}」なら、1行(1レコード)全体をそのまま指定したファイルに出力します。
ただし、結果を全て1つのファイルだけに出力するのであれば、awkの処理全体をリダイレクトでファイルに保存する方がスクリプトが単純になるでしょう。
図3の2つのコマンドラインは、どちらも「ls *.txt」の結果を行番号付きでfile.lstというファイルに出力しています。
コマンド実行例
ls *.txt | awk '{print FNR, $0}' > file.lst
(ls *.txtの結果を行番号付きでファイルに出力する)
ls *.txt | awk '{print FNR, $0 > "file.lst"}'
(ls *.txtの結果を行番号付きでファイルに出力する)
複数のファイルに出力する
print文を使って出力先のファイルを指定すると、awkの処理内容を複数のファイルに分けて出力できます(画面4)。
以下のtotal2.awkは、第211回のtotal.awkを一部変更したものです。order.txtを処理する前提のスクリプトです。
count[品名]には各品物(A〜D)の個数を、total[品名]には各品物の金額を集計しています。
ENDブロックでは変数fnに品名を表す1文字(A〜D)を入れた後、fnの内容の末尾に「.txt」という文字列を付けています。「sub(/$/, ".txt", fn)」という部分の処理です(※1)。
※1 行末($)だけを処理対象としているため文字列操作用のsub関数を利用した。この場合、gsub関数を使っても同じ結果になる(文字列を置き換える(第120回))。
BEGIN{ RS="" #レコードの区切りを空行とする FS="\n" #フィールドの区切りを改行とする } { for (i=1;i<=NF;i++){ split($i,d,"\t") #各行(フィールド)をタブ区切りで分割して配列dにセット if (d[1] == "注文"){ #「注文」の場合 count[d[2]] += d[4] #品物(注文行の2列目)ごとの個数を加算 total[d[2]] += d[3]*d[4] #品物(注文行の2列目)ごとの合計金額を加算 } } } END{ for (i in count){ fn = i #ファイル名用の変数fnにi(品名)をセット sub(/$/, ".txt", fn) #fnを「品名.txt」にする print "個数", count[i] > fn print "金額", total[i] >> fn } }
東川 雄一 注文 A 100 10 注文 B 200 5 西村 祐二 注文 B 200 10 南山 裕三 注文 C 300 15 注文 D 400 20 北岡 優四 注文 B 195 5 注文 C 295 15 注文 D 395 20 備考 優待チケットあり
筆者紹介
西村 めぐみ(にしむら めぐみ)
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.