初 心 者 用 awk 講 座 第 2 版 2000 年 2 月 25 日 文 責 斎 藤 輪 太 郎
1.はじめに awk はテキスト 処 理 言 語 です 与 えられたテキストファイル(cat や more で 中 味 を 見 ること ができるファイル)を 加 工 して 出 力 する 時 に 大 変 便 利 です このテキストではいくつかの 例 題 をこなしながら awk をある 程 度 使 えるようになることを 目 指 します 2. 行 単 位 の 処 理 の 基 本 以 下 のようなファイルを 作 ってみましょう ファイル 名 を food.txt とします I like an apple. He ate a banana. I cooked some corn. She has some donuts 次 に 以 下 のような awk スクリプトを 書 いてみましょう ファイル 名 を eat.awk とします /apple/ { print $0 Ringo!! } /corn/ { print $0 Toumorokoshi!!! } そして 以 下 のようにして awk を 起 動 します awk f eat.awk food.txt この 場 合 food.txt に 対 して eat.awk の 処 理 が 行 われます 以 下 のような 出 力 が 得 られまし たか? I like an apple...ringo!!! I cooked some corn...toumorokoshi!!! awk は 一 般 的 に 次 のような 形 式 をしています / 検 索 対 象 の 文 字 列 1/ { 文 字 列 1 があった 行 に 対 する 処 理 } / 検 索 対 象 の 文 字 列 2/ { 文 字 列 2 があった 行 に 対 する 処 理 } / 検 索 対 象 の 文 字 列 n/ { 文 字 列 n があった 行 に 対 する 処 理 } そして 次 のような 処 理 が 行 われます 1. 入 力 ファイル( 例 では food.txt)が 一 行 読 み 込 まれます 2.その 一 行 の 中 に 文 字 列 1~n がないか 調 べられます もし 文 字 列 が 見 つかればそれに 対 応 する 処 理 が 行 われます 3. 次 の 一 行 を 読 み 込 んで 同 じ 処 理 を 繰 り 返 します(1 に 戻 る) 2
入 力 ファイル awkスクリプト I like an apple. He ate a banana I cooked some corn She has some donuts. 一 行 ずつ 読 み 込 み /apple/ { print $0} /corn/ { print $0} 検 索 文 字 ハ ターン 検 索 文 字 ハ ターンにマッチした 時 の 処 理 では 先 ほどのスクリプトをみてみましょう /apple/ { print $0 Ringo!! } /corn/ { print $0 Toumorokoshi!!! } 1 行 目 は 読 み 込 んだ 行 に apple という 文 字 列 が 含 まれていたら その 行 ($0 で 表 す) と.Ringo!! を 表 示 させる 文 です 2 行 目 は 読 み 込 んだ 行 に corn という 文 字 列 が 含 まれていたら その 行 ($0 で 表 す) と.Toumorokoshi!!! を 表 示 させる 文 です 課 題 スクリプトファイルを 以 下 のように 変 えて 実 行 してみましょう /apple/ { print $4 Ringo!! } /corn/ { print $4 Toumorokoshi!!! } $4 は 処 理 中 の 行 の 4 番 目 の 単 語 を 表 します 課 題 スクリプトファイルを 以 下 のように 変 えて 実 行 してみましょう /some/ { print Ikutsukano $4} 3.BEGIN と END スクリプトを 実 行 する 前 の 初 期 設 定 を BEGIN の 中 に スクリプトが 終 了 したときの 処 理 を END の 中 に 書 く 事 ができます 以 下 のスクリプトを 実 行 してみましょう 入 力 ファイルは なんでもかまいません BEGIN { line_counter = 0 } { line_counter ++ } END { print line_counter } 3
入 力 ファイルの 行 数 が 表 示 されましたか? Awk ではC 言 語 などと 同 様 に 変 数 が 使 えます(C 言 語 の int line_counter;のようにあらか じめ 宣 言 する 必 要 はありません ) 1 行 目 で 行 数 を 数 える 変 数 line_counter を 0 にセットします 2 行 目 で 行 数 を 一 行 ずつカウントしていきます(++は line_counter の 値 を 1 つ 増 やすという 意 味 です) この 例 では / 検 索 対 象 の 文 字 列 / が 省 略 されています これを 省 略 すると 全 ての 行 が 処 理 の 対 象 となります 3 行 目 で 最 後 に 行 数 を 出 力 します 課 題 てきとうに 英 文 のテキストファイルを 作 りましょう( 少 し 長 めに) 次 に the という 単 語 が 含 まれる 行 数 をカウントするような awk スクリプトを 作 成 しましょう 4.コマンドラインからの 引 数 の 受 け 渡 し awk ではコマンドライン(awk 起 動 時 )に 変 数 = 値 の 形 があると この 変 数 が awk のスク リプトに 渡 されます 以 下 のような 一 行 のスクリプトを 書 いてみましょう このファイル 名 を p1.awk とします { print param1 $0 } 次 にこれを 以 下 のように 実 行 してみましょう awk f p1.awk param1= >> 入 力 ファイル 名 全 ての 入 力 ファイルの 行 の 先 頭 に >> が 付 けられるのが 分 かると 思 います 上 の 例 では コマンドラインの param1= >> で param1 という 変 数 に>>が 代 入 され print param1 $0 で 全 ての 入 力 行 の 前 に>>が(1つスペースを 置 いて) 付 加 されるようになっています 課 題 上 の 例 を 参 考 に コマンドラインである 文 字 列 を 受 け 取 り 全 ての 入 力 行 の 末 尾 に その 文 字 列 を 付 加 するようなスクリプトを 書 きましょう 5. 正 規 表 現 awk で 検 索 対 象 の 文 字 列 を 指 定 するときには 正 規 表 現 を 使 うことができます 正 規 表 現 は 簡 単 にかみくだいて 言 えば 1つの 表 現 で 複 数 のパターンを 指 定 できる 表 現 法 です(この 説 明 は 情 報 科 学 的 に 正 しいとはいえませんが) 例 えば 正 規 表 現 でma.eはmake,made,maleなど 複 数 のパターンを 表 します 正 規 表 現 を 使 うことによってパターンの 検 索 に 融 通 が 利 くようになるわけです ピリオド. は 任 意 の 一 文 字 を 表 します 正 規 表 現 の 例 をいくつか 挙 げておきます 4
^ 行 頭. 任 意 の1 文 字 [c 1 c 2..c n ] c 1 ~c n の 中 の 任 意 の 文 字 [^c 1 c 2..c n ] c 1 ~c n の 中 にない 任 意 の 文 字 r* r に 適 合 する 文 字 列 の 0 個 以 上 の 連 続. ピリオド( で 正 規 表 現 の 特 別 な 意 味 をなくす) ^The は 行 頭 の The にマッチします [acgt]は a,c,g,t の 任 意 の 一 文 字 にマッチします [acgt]*は a,c,g,t から 構 成 される 任 意 の 長 さの 文 字 列 ( 長 さ0も 含 む)にマッチします [a-z]はアルファベットの 小 文 字 にマッチします 課 題 英 単 語 The および the がある 行 を 表 示 するスクリプトを 書 きましょう 課 題 英 単 語 The が 先 頭 にある 行 を 表 示 するスクリプトを 書 きましょう The の 前 にスペ ースがあっても 許 すものとします 課 題 アルファベット 空 白 以 外 の 文 字 が 含 まれる 行 を 表 示 するスクリプトを 書 きましょ う 6.フィールドセパレータ $1,$2,$3 は 処 理 している 行 の1 番 目 の 単 語 2 番 目 の 単 語 3 番 目 の 単 語 を 表 していまし た より 正 確 に 言 えば 入 力 行 のスペースで 区 切 られた 部 分 が$1,$2,$3 の 境 目 になってい ました Awk ではこの 境 目 となるもの(フィールドセパレータ)を 変 えることができます 次 のテキストファイルを 作 って 下 さい ファイル 名 を names.txt とします Tetsuya Iida @ center @ 2 Atsunori Inaba @ right @ 41 Atsuya Furuta @ catcher @ 27 そして 次 のような awk スクリプトをつくってみましょう ファイル 名 を names.awk とし ます BEGIN { FS= @ } { print $2 } そしてこのスクリプトを 実 行 して 下 さい 次 のような 実 行 結 果 が 得 られるはずです center right catcher この 場 合 @ がフィールドセパレータになっています 従 って center の 列 が 第 2フィール ド($2)に 相 当 するのです なお BEGIN の 行 を 書 かなくても awk を 呼 ぶときに 5
awk F@ -f names.awk names.txt とすれば 同 じように @ がフィールドセパレータとなります 課 題 住 所 録 を3 人 分 ほど 作 ってみましょう 名 前 住 所 電 話 番 号 を, で 区 切 って1 行 に1 人 分 ずつ 書 き awk を 使 って 電 話 番 号 だけを3 人 分 表 示 してみましょう 7.awk 組 み 込 み 変 数 awk にはいくつかもとから 意 味 を 持 つ 変 数 があります 今 まで 何 気 なく 使 ってきた$0,$1,$2 などもそうです NR は 現 在 処 理 中 の 行 が 何 行 目 かを 表 します 次 のスクリプトを 見 てみま しょう { print NR $0 } これはテキストファイルの 中 味 を 行 番 号 付 きで 表 示 します 実 際 に 確 かめてみまし ょう print は 画 面 上 に 変 数 の 値 文 字 列 などを 出 力 する 命 令 ですが 変 数 と 文 字 列 (ダブルク オテーション で 囲 む)を 続 けて 書 くことができます 上 の 例 では 変 数 NR, 文 字 列 変 数 $0 の 中 味 が 続 けて 表 示 されます 変 数 NR を 使 って 偶 数 番 目 の 行 を 出 力 することもできます NR % 2 = = 0 { print $0 } NR % 2 = = 0 は NR を2で 割 った 余 りが0なら つまり 偶 数 なら という 条 件 文 です こ の 条 件 に 合 った 行 が 出 力 されることになります このように{ 処 理 }の 左 側 には 文 字 列 による 条 件 分 だけでなく 数 式 による 条 件 文 を 書 く ことができます 課 題 1 行 目 から 50 行 目 までを 行 番 号 付 きで 表 示 するスクリプトを 書 いてみましょう この 他 に 組 み 込 み 変 数 として NF(その 行 のフィールド 数 )などがあります 調 べてみましょ う 8.awk 組 み 込 み 関 数 awk にはいくつか 便 利 な 関 数 が 用 意 されています いくつかを 紹 介 しておきましょう gsub(r,s,t) 文 字 列 t の 中 で r に 適 合 するもの 全 てを s で 置 換 し 置 換 数 を 返 す ファイル 名 genome.awk { line = $0; 6
nsub = gsub( gene, genome, line); print line; print nsub; } コマンド 行 echo Analysis of the genes awk f genome.awk 1 このように echoなどのコマンドの 出 力 結 果 をパイプ でつないで awk への 入 力 とするこ とができます 実 行 結 果 Analysis of the genomes length(t) 文 字 列 t の 長 さを 返 す printf 書 式 に 従 って 文 字 列 や 数 値 を 表 示 する C 言 語 のものとよく 似 ているので 詳 しくは 各 自 調 べて 下 さい split(s,a,fs) fs をフィールドセパレータとして s を 配 列 a に 分 解 し できた 配 列 数 を 返 す s は a[1],a[2],a[3], に 分 解 される ファイル 名 skiing.awk { line = $0; split(line, array, - ); print array[3]; } コマンド 行 echo I-LIKE-SKIING-IN-WINTER awk f skiing.awk 実 行 結 果 SKIING substr(s,p,n) s の p 番 目 から 始 まる 長 さ n の 部 分 文 字 列 を 返 す ファイル 名 mothers.awk 7
{ print substr($0, 2, 5) } コマンド 行 echo Mothers awk f mothers.awk 実 行 結 果 other 課 題 ある 英 文 テキストファイルの 中 にアルファベット a,c,g,t がいくつずつ 含 まれている かを 表 示 する awk スクリプトを gsub を 使 って 書 きましょう 9. 配 列 substr 関 数 のところでちょっとでてきましたが awk では 配 列 変 数 を 使 うことができます 配 列 変 数 は 変 数 名 に 数 字 をつけた 変 数 で 似 たような 性 質 のデータを 一 元 的 に 管 理 できる ようになります 使 い 方 は 変 数 名 [ 整 数 ] で 例 えば line[2] = number 2 とすると line[2]に number 2 と いう 文 字 列 が 代 入 されます(C 言 語 のように 配 列 の 宣 言 をする 必 要 はありません) [ ]の 中 の 整 数 は 変 数 にすることもできます 例 えば i = 3; line[i] = number three とすれば line[3]の 中 に number three という 文 字 列 が 代 入 されます 10.if 文 if 文 はある 条 件 にあったときに 指 定 した 処 理 を 実 行 します 書 式 は 以 下 の 通 りです if ( 条 件 ) 条 件 に 合 ったときの 処 理 else 条 件 に 合 わなかったときの 処 理 省 略 可 偶 数 番 目 の 行 を 表 示 するスクリプトは NR % 2 = = 0 { print $0 } でしたが { if (NR % 2 = = 0)print $0 } と 書 いても 同 じように 処 理 されます また /the/ { print $0 } は { if ($0 ~ /the/) print $0 } と 同 じです 8
課 題 連 続 して 重 複 した 行 を1つにまとめて 出 力 するスクリプトを 書 きましょう I like driving. I like driving. I like playing soccer. I like playing soccer. I like playing soccer. ちょっと 難 しいと 思 われる 方 のためにヒントです たった 2 行 で 書 けるはずです NR = = 1 { prev = $0 ;..??..??.?.?} NR > 1{ if (prev!=?????.???? } 11 for 文 C 言 語 などでお 馴 染 みの for 文 が awk でも 使 えます for 文 はある 条 件 が 満 たされている 間 指 定 された 処 理 を 実 行 する 命 令 です 書 式 は 以 下 の 通 りです for( 初 期 設 定 ; 処 理 継 続 条 件 ; 更 新 処 理 ){ } 処 理 の 内 容 for 文 に 入 ったときに 最 初 に 初 期 設 定 が 実 行 されます 処 理 継 続 条 件 に 合 う 間 処 理 の 内 容 が 実 行 されます 処 理 の 内 容 を 終 えるたびに 更 新 処 理 が 実 行 されます 次 の awk スクリプトは 各 行 のフィールドを, で 分 けるように 加 工 して 出 力 します ファイル 名 sepat.awk { for(i = 1;i < NF;i ++){ printf( %s,, $i); } print $NF; } 入 力 ファイル 名 Bluebird 205PS Nissan Galant 205PS Mitsubishi Prelude 220PS Honda 9
Supra 280PS Toyota 実 行 結 果 Bluebird,205PS, Nissan Galant,205PS, Mitsubishi Prelude,220PS,Honda Supra,280PS,Toyota for(i = 1;i < NF;i ++)で i の 値 を 1 から NF( 一 行 のフィールド 数 )の 直 前 まで(NF-1 まで) 1 ずつ(i++) 増 やします printf( %s,, $i)で i 番 目 のフィールドが, とともに 出 力 されます 10
応 用 課 題 ある 文 字 列 を 見 つけたらその 行 と その 後 5 行 を 表 示 するスクリプトを 作 って 下 さい 解 法 ある 行 を 見 つけて 行 を(5 行 分 ) 表 示 中 であることを 示 す 変 数 (フラッグ)を 使 うと 便 利 です BEGIN{ to_print = 0; } / 検 索 文 字 列 / { to_print = 5; } to_print > 0 {??????????? } 応 用 課 題 ある 文 字 列 を 見 つけたらその 行 と その 前 5 行 を 表 示 するスクリプトを 作 って 下 さい 解 法 配 列 を 使 って 行 を 記 憶 していくと 便 利 です { for(i = 5;I > 0;I --)prevline[i] = prevline[i 1]; prevline[0] = $0; } 応 用 課 題 入 力 ファイルが 次 のような 構 成 になっていると 仮 定 します For sale Product = car レコード1 Premera 90 Price = 1,000,000 For rent Product = car レコード2 Legnum GDI Price = 10,000 / day For sale Product = motorcycle Zephyr 400 Price = 400,000 For sale Product = car Maclauren F1 Price = 90,000,000 (1) For sale のレコード 全 体 を 全 て 出 力 するスクリプトを 書 きましょう (2) Product が car のレコードを 全 て 出 力 するスクリプトを 書 きましょう 11