1. はじめに
前回は「OSS ツール Part-1」と題して、IBM i のオープンソース環境で利用可能な様々なツールの中から、圧縮・解凍ツール、Web系ツール、データベース接続ツールなどを紹介しました。
圧縮・解凍ツールとして、tarやgzip/bzip2、zip/unzip、7-zip。Web系ツールとして、curlとjson形式のファイルを処理するjq。データベース接続ツールとして、unixODBCとibm-iaccess。それぞれについて使用例を交えて解説しましたが、いかがだったでしょうか。
Linuxユーザーにとっては馴染みのあるツールばかりだったと思いますが、これらのツールがIBM i で利用できることを知っていただけたと思います。また、IBM i 技術者の皆様には、新鮮さと驚きを感じていただくとともに、大いに興味を持っていただけたのではと思っています。
第9回となる今回は、前回に続き「OSS ツール Part-2」として、テキスト処理に焦点を当て、grep、sed、awkといった強力なツールを紹介します。これらのツールは、大量のテキストデータを効率的に処理する上で非常に役立ちます。特に、正規表現と組み合わせることで、複雑な条件にも対応できる柔軟性を備えています。
IBM i を利用する皆様にとっても、これから紹介するテキスト処理ツールは、ログ分析、データ抽出、ファイル変換など、様々な場面で活用できるはずです。Linuxユーザーにとっても、IBM i 環境で使い慣れたツールを使用できることは、大きなメリットとなるでしょう。
本記事では、正規表現の基本的な解説から始め、grep、sed、awkの使い方を具体例を交えながら説明します。これらのツールを使いこなせば、IBM i でのテキスト処理を大幅に効率化できるはずです。
記事内で使用した各ツールのバージョンは以下の通りです。環境によっては、別途導入が必要な場合があるので注意してください。
ツール | バージョン |
---|---|
grep, egrep | grep (GNU grep) 3.0 |
sed | sed (GNU sed) 4.4 |
awk | GNU Awk 4.1.4 |
それでは始めていきましょう。
2. 正規表現
各テキスト処理ツールの解説を始める前に、どのツールでも使用する非常に大切な正規表現についてお話しておきます。
そもそも、正規表現とは何でしょうか。用語としては皆さん様々なところで目にしていると思いますし、知らず知らずのうちに利用していたということもあるかもしれませんね。
正規表現は、文字列のパターンを表す式のことです。そして、各ツールと正規表現を一緒に使うことで、大量のテキスト処理や検索を効率良く行うことが可能になります。
実は、正規表現はとても複雑で、それだけで一冊の本が出版されている(オライリー・ジャパンの「詳説 正規表現」)ほどです。本記事で正規表現をくまなく解説することは当然ながら不可能なので、ごく基本的な部分にのみ焦点をあてて解説したいと思います。
最も簡単な正規表現
第3回「シェルと基本コマンド」で、systemコマンドの例を取り上げました。
上記は、IBM i のQGPLライブラリー内にある物理ファイル(PF)の数を求めているのですが、grepに指定された以下の部分で正規表現が使われています。
- '*FILE'
- 'PF'
最初のgrepでは、パイプ処理で渡されたDSPOBJDの結果から、文字列「*FILE」を含む行を抽出しています。2番目のgrepでは、文字列「PF」を含む行を抽出して「wc」に渡しています。
上記の「*FILE」も「PF」も、正規表現として有効であり、grepに指定するとその文字列そのものを検索してくれます。
この正規表現を使うと、grepは文字列そのものを検索するのですが、それが検索するテキスト内のどこにあるかは問題ではありません。「特定の文字を含んだ行を探したい」というのは最も多い要望でもあるので、一番多く利用される正規表現だと思います。
行頭と行末
では、先頭が文字列「OSS」で始まる、あるいは行末が「OSS」で終わる行を検索したい場合は、どうすれば良いのでしょうか。
正規表現では、メタ文字というものを使って様々な条件を指定できます。行頭と行末を表すメタ文字は以下の通りです。
意味 | メタ文字 | 備考 |
---|---|---|
行頭 | ^ | キャレット |
行末 | $ | ドル記号 |
「^OSS」は、「行が文字列 OSS で始まる」という意味であり、「OSS$」は「行が OSS で終わる」という意味になります。
任意の一文字と任意文字
文字列の間にどの文字が入っていても検索対象としたい場合は、以下のメタ文字を使います。
意味 | メタ文字 | 備考 |
---|---|---|
任意の一文字 | . | ドット |
任意の任意文字 | * | アスタリスク |
では、「'c.t'」と「'c*t'」で、それぞれの違いを見てみましょう。
パターン | 文字列 | 'c.t' | 'c*t' |
---|---|---|---|
1 | cat | マッチ | マッチ |
2 | ct | アンマッチ | マッチ |
3 | caat | アンマッチ | マッチ |
まず、「.(ドット)」は任意の一文字なので、正規表現で指定した箇所には必ず何らかの一文字が存在していなければなりません。「'c.t'」は、間に何らかの文字が一文字ないといけないので、パターン1のみマッチします。
「'c*t'」は、任意の任意文字なので、間に文字がなくても、あるいは、一文字でも複数文字でも文字があればマッチします。
さて、ここで「おや?」と気づかれた方はいらっしゃるでしょうか。先ほどのgrepで、アスタリスクを含む文字列「*FILE」を指定しているかのように記述しました。ところが、実は、これは文字列「FILE」を含む(文字としてのアスタリスクは含まない)行を選択しているのです。
正規表現における「*」の意味は、任意の任意文字でした。つまり、「*FILE」は、検索行内のどこの位置でも良いので「FILE」という文字列を検索するという正規表現だったのです。
したがって、「*」も検索対象文字列とするには、エスケープ処理(詳細は後述)が必要です。以下のように指定するのが正解です。
直前のパターンの繰り返し
直前の文字(パターン)が繰り返しているかどうかを表現するには、以下のメタ文字を使います。
意味 | メタ文字 | 備考 |
---|---|---|
一回以上繰り返し | + | プラス |
0回または一回繰り返し | ? | クエスチョン |
では、「'ca+t'」と「'ca?t'」で、その違いを見てみましょう。
パターン | 文字列 | ‘ca+t’ | ‘ca?t’ |
---|---|---|---|
1 | cat | マッチ | マッチ |
2 | ct | アンマッチ | マッチ |
3 | caat | マッチ | アンマッチ |
メタ文字「+」は「一回以上繰り返し」なので、パターン2以外はマッチします。ですが、「?」は「繰り返しがなくてもよいが、あっても一回だけ」なので、パターン3はマッチしないという意味になります。
ちなみに、grepではメタ文字「+」と「?」は、基本的に使えません。「-E」をつけるかegrepを使用してください。
$ egrep 'ca+t' ./text.txt
回数を指定した繰り返し
特定の回数を指定した繰り返しは、波括弧({ および })で囲って指定します。
パターン | 正規表現 | 備考 |
---|---|---|
1 | {n} | n回繰り返し |
2 | {n,} | n回以上繰り返し |
3 | {s,e} | s回以上、e回以下繰り返し |
以下のように使用します。
パターン | 文字列 | 'a{1}' | 'a{2}' | 'a{1,}' |
---|---|---|---|---|
1 | cat | マッチ | アンマッチ | マッチ |
2 | caat | マッチ | マッチ | マッチ |
メタ文字そのものを検索
上記で紹介したメタ文字(^ $ . * + ?)を、文字そのものとして検索対象としたい場合もあると思います。その場合は、検索したいメタ文字の前に「\」(バックスラッシュ)を付けてエスケープ処理する必要があります。
例を見てみましょう。
パターン | 文字列 | '$' | '\$' |
---|---|---|---|
1 | $100 | マッチ | マッチ |
2 | 2500 | マッチ | アンマッチ |
「'$'」は行末を表すメタ文字だけを含んでいるので、例えばテキスト・ファイルに両パターンが別々の行で入っていれば、行末は必ずあるので両方ともマッチします。
エスケープ処理をした「'\$'」では、文字としての「$」を検索するのでパターン2はマッチしません。
文字クラス
角括弧([ と ])を使用すると、文字のリストを作成できます。
パターン | 文字列 | 'than' | 'then' | 'th[ae]n' |
---|---|---|---|---|
1 | than | マッチ | アンマッチ | マッチ |
2 | then | アンマッチ | マッチ | マッチ |
三文字目が a でも e でもマッチさせたい場合は、その文字を角括弧で囲むことにより、両方にマッチするようになります。
また、アルファベットや数字のみに限定するには、以下のパターンを指定します。
パターン | 正規表現 | 意味 |
---|---|---|
1 | [a-z] | 英小文字 |
2 | [A-Z] | 英大文字 |
3 | [0-9] | 数字 |
4 | [a-zA-Z0-9_] | 英小文字、英大文字、数字およびアンダースコア |
否定するには、[] 内の先頭に「^」を追加します。
パターン | 正規表現 | 意味 |
---|---|---|
1 | [^a-z] | 英小文字以外 |
2 | [^A-Z] | 英大文字以外 |
3 | [^0-9] | 数字以外 |
4 | [^a-zA-Z0-9_] | 英小文字、英大文字、数字およびアンダースコア以外 |
OR条件
いくつかの文字列のどれかにマッチするかどうかを表現するには、「|」(パイプ)を使います。
パターン | 文字列 | (http|https) | (ftp|ssh) |
---|---|---|---|
1 | http://〜 | マッチ | アンマッチ |
2 | https://〜 | マッチ | アンマッチ |
3 | ftp://〜 | アンマッチ | マッチ |
4 | ssh://〜 | アンマッチ | マッチ |
よくあるパターン
では、これまでの説明を踏まえて、いくつかのパターンを見ていきましょう。
数字のチェック
まず、文字列が数字のみで構成されているかどうかをチェックする際に使用できる正規表現を見てみましょう。
では、この正規表現を分解してみましょう。
正規表現 | 意味 |
---|---|
^ | 行頭 |
[0-9] | 数字 |
+ | 数字を一回以上繰り返し |
$ | 行末 |
メタ文字は「+」なので、1桁以上であれば何桁でも構いません。
桁数チェック
では、入力文字数が3文字以上5文字以下にしたい場合、正規表現はどうすればよいでしょうか。任意文字の繰り返し範囲を指定できれば良いはずです。3文字以上5文字以下を表現する正規表現は以下の通りです。
これも分解してみましょう。
正規表現 | 意味 |
---|---|
^ | 行頭 |
. | 任意文字 |
{3,5} | 3以上5以下で繰り返し |
$ | 行末 |
郵便番号のチェック
続いて、郵便番号のチェックに使用される正規表現を考えてみましょう。
日本の郵便番号として正しいものは、以下の条件を含んだものです。
- 数字およびハイフン(-)で構成
- 数字3桁 + ハイフン(-)+ 数字4桁
この正規表現は以下です。
この正規表現も、これまでと同様に分解してみましょう。
正規表現 | 意味 |
---|---|
^ | 行頭 |
[0-9] | 数字([0-9]) |
{3} | 数字が3回繰り返される(1桁目から3桁目まで数字) |
– | ハイフンが4桁目に現れる |
[0-9] | 数字([0-9]) |
{4} | 数字が4回繰り返される(5桁目から8桁目まで数字) |
$ | 行末 |
これは、検査する文字列が「xxx-xxxx」(xは、0-9の数字)という形式であることを表現する正規表現です。郵便番号として正しいかどうかをチェックする際は、この表現を使用できます。
日付形式のチェック
日付形式のチェックも考えてみましょう。今回は、ISO形式(yyyy-mm-dd)となっているかどうかをチェックできる正規表現です。
少し複雑ですね。これも分解してみましょう。
正規表現 | 意味 |
---|---|
[0-2] | 西暦の1桁目は、0,1,2のどれか |
[0-9]{3} | 西暦の2桁目から4桁目は、0から9の数字 |
– | ハイフンが4桁目に現れる |
(0[1-9]|1[0-2]) | 01-09まで。あるいは、10-12まで |
– | ハイフンが8桁目に現れる |
(0[1-9]|[12][0-9]|3[01]) | 01-09まで。あるいは、10-29まで。あるいは、30-31まで |
このように分解してみることで理解が進むと思います。ただ、この正規表現は完全ではありません。例えば、2月31日や11月31日でも可能だからです。その点を踏まえて、皆さんも色々と試してみてください。
注)実は、[0-9]の代替表現として、「\d」を使用することが可能なはずですが、筆者のIBM i 環境では不可でした。本来であれば、上記の正規表現は以下のように記述可能です。
ここまで説明した正規表現は基本的なもののみですが、この組み合わせで多くの表現ができるようになるはずです。また、正規表現はSQLのREGEXP_LIKE関数でも指定できるので、そちらでも試してみてください。例えば、以下のようなSELECT文を実行できます。
REGEXP_LIKE関数をwhere句で使っていますが、CSTELEフィールドの値を正規表現を使って、その条件に合うレコードを選択しています。どのような電話番号が選択対象となるのか、もうお分かりですよね。
3. grep, egrep
それでは、「シェルと基本コマンド」で少し触れたgrepについて改めて解説しようと思います。
先ほども出てきましたが、grepはテキスト・ファイルから文字列を検索するコマンドです。通常は以下のように指定します。
指定可能な主なオプションは以下の通りです。
No. | オプション | 意味 |
---|---|---|
1 | -i | 大文字・小文字を区別せずに検索 |
2 | -v | 検索文字列と一致しない行を選択 |
3 | -c | 一致した行数を表示 |
4 | -l | 一致した行を含むファイル名のみ表示 |
「検索する文字列」には、正規表現を指定できます。これにより複雑な検索条件を指定可能です。指定できる正規表現は「基本正規表現」ですが、先ほど解説したサンプルや、読者の方がネット等で調べて得られる情報は「拡張正規表現」の場合がほとんどです。
拡張正規表現を有効にするには -E オプション、もしくは、egrepコマンドを使用してください。grepでは利用できない正規表現などもあるので、以降はegrepを使用します。
上記は、ファイル access_log 内で文字列「chrome」を含む行を検索します。大文字と小文字は区別するので、「Chrome」を含む行は検索対象外です。大文字・小文字を区別せず、「Chrome」も「CHROME」も検索対象とする場合は、オプション i を指定します。
「chrome」を含まない行を検索する場合は次のように指定します。
複数のファイルをまとめて検索するには以下のように指定します。
上記は、「access_log」で始まる複数のファイルを串刺しで検索して結果を表示します。一致した各行の先頭には、その行を含むファイル名が追記されます。
検索した文字列を含むファイル名だけを知りたい場合はオプション l を付けましょう。
では、次に、正規表現を使用したegrepの具体的な例を考えてみましょう。
オブジェクトの一覧を取得
「シェルと基本コマンド」では、以下のコマンドを用いて、ある特定のオブジェクトの数を出力する例を紹介しました。
行数はオプション -c でも取得可能なので、以下でも同じ結果を得られます。
さて、上記は「*FILE」オブジェクトのみの選択だったから良かったのですが、全てのオブジェクトの一覧を取得するにはどうすれば良いでしょうか。
IBM i の全てのオブジェクトは、「*」で始まるオブジェクト・タイプを持っています。そこで、「*」文字のみを含む行を選択するように記述すれば良さそうです。
しかし、これでは、行のどこであっても「*」を含んでいれば対象行となってしまいます。実際、DSPOBJDの出力する情報は、オブジェクト情報とは関係ない行に「*」を含んだ行が存在しています。
そこで、もう少し条件を付けてみましょう。DSPOBJDが出力する行のうち、オブジェクトを表す行は以下の通りです。
TEST01 *PGM RPGLE 126976 TEST02 *PGM RPGLE 106496
よく見ると分かるように、オブジェクト対応の「*」は、18桁に存在しています。つまり、行の先頭から17文字は任意文字で、18桁目が「*」という条件を指定できればよいわけです。
これを実現する正規表現は以下です。
また、分解してみましょう。
正規表現 | 意味 |
---|---|
^ | 行頭 |
. | 任意の一文字 |
{17} | 任意の一文字を、17回繰り返し |
\* | 文字としての * |
これを反映して、全てのオブジェクトの一覧を出力してみましょう。
TEST01 *PGM RPGLE 126976 TEST02 *PGM RPGLE 106496 EXCELAFBA *SQLPKG 98304 ISQLAFBA *SQLPKG 98304 MSQRY3AFBA *SQLPKG 98304 PYTHONAFBA *SQLPKG 98304 PYTHONAVBA *SQLPKG 98304 :
オブジェクトの数を表示するには以下を実行します。
130
一覧からさらに条件を追記して絞り込む場合は、結果をパイプ処理で別のegrepに渡して正規表現で検索を行います。オブジェクト名が Q 以外で始まるもののみを抽出してみましょう。オブジェクト名は各行の3桁目から始まっているので、3桁目が Q 以外という正規表現を使います。
16
3桁目が Q のものを選択すると、
114
合計すると 130 なので合っていそうですね。
4. sed
第5回「OSS Part-2」で紹介したvimを覚えていらっしゃいますか。vimはテキスト・エディターで、テキスト・ファイルの内容を画面に表示しながら対話式に編集を行うことができるツールです。Windowsのメモ帳に相当するものです。
基本的に、OSSで提供されている様々なサーバーの構成ファイルやログファイルなどはテキスト・ファイルなので、vimなどを使う編集が可能です。また、CSVファイルなどもテキスト・ファイルなので、vimを使えば内容の確認等も簡単に行うことができるはずです。
一般的にはvimを使うことで、ほとんどのテキスト・ファイルの操作は可能です。一方で、対話式で行うために、操作のたびにファイルを開き、修正等の箇所を目視で確認し、ひとつひとつ操作を行っていかなければなりません。単純な作業であるにもかかわらず、場合によっては修正箇所が多いために膨大な時間を費やさなくてはならないケースもありえます。
例えば、以下のようなケースを考えてみましょう。
- ある単語を別の単語で置き換えたい
- 空白行を削除したい
- 行頭と行末にあるブランクを削除したい
上記作業を行いたいテキスト・ファイルが10数行程度の小さなものであれば、vimを使って操作するのが最も早いと思います。しかし、数千行、数万行のテキスト・ファイルになるとそうはいきません。通常、このケースになるとプログラムを作成するという判断がされると思いますが、sedを使えばプログラミングなしで同じことを行うことが可能になります。
sedは、Stream EDitorの略称で、その名前の通りテキストを行単位で順次処理(ストリーム処理)するエディターです。vimと異なり、対話式で動くエディターではありません。
例えば、あるテキスト内の「AS/400」という単語を「IBM i」で一気に置き換えたい場合は、以下のように指定します。
text01.txt の内容が以下だとします。
1988年に発表以来、多くのお客様に愛されてきました。
では、上記のsedを実行してみましょう。
IBM i は、基幹システムを実行するために特別に設計されたコンピュータです。
1988年に発表以来、多くのお客様に愛されてきました。
「AS/400」が「IBM i」に置き換わって出力されましたね。
では、sedについて、簡単に説明しましょう。sedコマンドは以下のように指定します。
sed [オプション] 'コマンド' ファイル名
主なオプションは以下の通りです。
No. | オプション | 意味 |
---|---|---|
1 | -i | 元のファイルを直接編集する |
2 | -i.<名前> | 直接編集する場合にバックアップを取る |
3 | -n | 対象となる行だけを表示 |
次に、それぞれのアクション別 'コマンド' の書き方についてまとめておきます。
アクション | コマンド | 備考 |
---|---|---|
置換 | 's/変換前文字列/変換後文字列/' | 行内の最初に見つかった文字列のみ変換対象 |
's/変換前文字列/変換後文字列/g' | 行内の全ての対象文字列を置換 | |
削除 | '/正規表現/d' | 正規表現で対象となる行を削除 |
特定の行のみ | -n 's/変換前文字列/変換後文字列/p' | -n と p を組み合わせて指定 |
では、上記を踏まえて、先ほどのsedコマンドを再確認しましょう。
上記の 'コマンド' の部分を分解してみると、以下のようになります。
設定値 | 意味 |
---|---|
s | 置換 |
/ | 区切り文字 |
AS\/400 | 置換前の文字 AS/400 の / は区切り文字ではないのでエスケープ処理 |
IBM i | 置換後の文字 |
/ | 区切り文字 |
sedのコマンドで「/」は特別な意味を持つので、「AS/400」のスラッシュはエスケープ処理が必要です。忘れないようにしてください。
では、もう少し複雑なテキスト(text02.txt)を例に、各コマンドと出力の違いを見ていきます。
1988年に発表以来、多くのお客様に愛されてきました。
2024年現在も、AS/400 は進化を続けて、従来の AS/400 技術者だけでなく、
他プラットフォームの技術者にも魅力的なコンピュータとして認知されています。
's/AS\/400/IBM i/'
IBM i は、基幹システムを実行するために特別に設計されたコンピュータです。
1988年に発表以来、多くのお客様に愛されてきました。
2024年現在も、IBM i は進化を続けて、従来の AS/400 技術者だけでなく、
他プラットフォームの技術者にも魅力的なコンピュータとして認知されています。
全ての行が出力され、各行の最初の「AS/400」が「IBM i」に変換されています。4行目にある2番目の「AS/400」は置換されずにそのまま出力されます。
's/AS\/400/IBM i/g'
IBM i は、基幹システムを実行するために特別に設計されたコンピュータです。
1988年に発表以来、多くのお客様に愛されてきました。
2024年現在も、IBM i は進化を続けて、従来の IBM i 技術者だけでなく、
他プラットフォームの技術者にも魅力的なコンピュータとして認知されています。
全ての行が出力され、今度は各行にある全ての「AS/400」が「IBM i」に変換されています。
'/^$/d'
AS/400 は、基幹システムを実行するために特別に設計されたコンピュータです。
1988年に発表以来、多くのお客様に愛されてきました。
2024年現在も、AS/400 は進化を続けて、従来の AS/400 技術者だけでなく、
他プラットフォームの技術者にも魅力的なコンピュータとして認知されています。
空白行を除いた全ての行が出力されます。先ほどの正規表現を思い出してください(^ は行頭、$ は行末)。
-n 's/AS\/400/IBM i/p'
IBM i は、基幹システムを実行するために特別に設計されたコンピュータです。
2024年現在も、IBM i は進化を続けて、従来の AS/400 技術者だけでなく、
変換対象の文字列を含む行のみが表示されます。2行めの2番目の「AS/400」は置換されていません。
-n 's/AS\/400/IBM i/pg'
IBM i は、基幹システムを実行するために特別に設計されたコンピュータです。
2024年現在も、IBM i は進化を続けて、従来の IBM i 技術者だけでなく、
gコマンドを指定することで、同一行内の全ての置換文字列が対象になります。
実際のファイルの変更
実は、sedコマンドは処理結果を標準出力である画面に表示しますが、実際のファイルの中身の変更は行いません。これを実行するにはオプション -i を使用します。また、直接編集する際にファイルのバックアップを取るように指定することも可能です。
上記を実行すると、変更前のファイルを text02.txt.bak という名前でバックアップを取ったうえで、text02.txt が以下のように直接書き換えられます。
1988年に発表以来、多くのお客様に愛されてきました。
2024年現在も、IBM i は進化を続けて、従来の IBM i 技術者だけでなく、
他プラットフォームの技術者にも魅力的なコンピュータとして認知されています。
IFS上のテキスト・ファイルの操作は、sedを使うことで多くの時間を節約できます。特に、IBM i 技術者で、テキスト・ファイルを EDTF コマンドで操作されている方は、ぜひ sed コマンドを使ってみてください。
5. awk
もうひとつのツールであるawkについても見ていきましょう。
awkは、テキスト処理用のプログラミング言語で、名前の由来は開発した三人の頭文字です。プログラミング言語なので文法的な詳細を解説することはできませんが、先ほど解説したsedのように気軽に利用できる機能もあるので、今回はそれを紹介したいと思います。
テキスト・データの処理
まず、以下のようなテキスト・ファイル tecsmp.txt の処理を考えてみましょう。これは、IBM i のデータベースの内容を、CPYTOIMPFコマンドを使用して作成したものです。
01010 アラカワヤッキョク 荒川薬局 東京都荒川区 町屋4-1-2 01 01020 コウトウビヨウガッコウ 江東美容学校 東京都江東区 青海1-4-2 01 01030 スミダビューティー 墨田ビューティー 東京都墨田区 向島4-5-9 01 01040 エドガワビヨウサロン 江戸川美容サロン 東京都江戸川区 葛西1-1-2 01 01050 チュウオウショクヒンカン 中央食品館 東京都中央区 日本橋4-1-3 01 01060 サロン・ドミナト サロン・ド港 東京都港区 新橋西4-5-6 01 01070 キタエイヨウセンモンガッコウ 北栄養専門学校 東京都北区 赤羽西5-8-9 01 01080 ブンキョウヤクザイ 文京薬剤 東京都文京区 千駄木1-2-3 01 01090 シナガワケショウヒン 品川化粧品 東京都品川区 東品川5-3-2 01 01110 オオタソウゴウケンキュウジョ 大田総合研究所 東京都大田区 大森4-6-5 01
各項目は tab で区切られており、先頭から、顧客番号、顧客カナ名、顧客漢字名、住所1、住所2、地区コードで構成されています。
例えば、このファイルから顧客番号と顧客漢字名だけを取り出したい場合、awkでは以下のように記述します。
bash-5.1$ awk '{print $1, $3}' tecsmp.txt 01010 荒川薬局 01020 江東美容学校 01030 墨田ビューティー 01040 江戸川美容サロン 01050 中央食品館 01060 サロン・ド港 01070 北栄養専門学校 01080 文京薬剤 01090 品川化粧品 01110 大田総合研究所
awkコマンドの基本構文は、
awk '{コマンド}' 対象ファイル
です。先ほどのサンプルでお分かりのように、タブで区切られた値は左から順に、$1、$2 ・・・で参照可能です。今回は、1番目の顧客番号と3番目の顧客漢字名を対象とし、printコマンドを使って、この2つを表示しています。
もちろん、awkは処理データをパイプ処理で受け取ることもできるので、例えば、漢字名に「美容」を含むデータのみを対象としたい場合は、egrepで行を絞り込んで、awkにパイプ処理で渡します。
bash-5.1$ egrep '美容' tecsmp.txt | awk '{print $1, $3}' 01020 江東美容学校 01040 江戸川美容サロン
CSVファイルの処理
では、以下のデータを含むCSVファイル tecsmp.csv ではどうでしょうか。
"01010","アラカワヤッキョク","荒川薬局 ","東京都荒川区","町屋4−1−2","01" "01020","コウトウビヨウガッコウ","江東美容学校 ","東京都江東区","青海1−4−2","01" "01030","スミダビューティー","墨田ビューティー ","東京都墨田区","向島4−5−9","01" "01040","エドガワビヨウサロン","江戸川美容サロン ","東京都江戸川区","葛西1−1−2","01" "01050","チュウオウショクヒンカン","中央食品館 ","東京都中央区","日本橋4−1−3","01" "01060","サロン・ドミナト","サロン・ド港 ","東京都港区","新橋西4−5−6","01" "01070","キタエイヨウセンモンガッコウ","北栄養専門学校 ","東京都北区","赤羽西5−8−9","01" "01080","ブンキョウヤクザイ","文京薬剤 ","東京都文京区","千駄木1−2−3","01" "01090","シナガワケショウヒン","品川化粧品 ","東京都品川区","東品川5−3−2","01" "01110","オオタソウゴウケンキュウジョ","大田総合研究所 ","東京都大田区","大森4−6−5","01"
各項目をカンマ(,)で区切ったcsv形式のファイルをawkで処理する場合は、オプション -F に続けて、カンマを区切り文字として指定することで、各項目を取り出だせます。
bash-5.1$ awk -F',' '{print $1, $3}' tecsmp.csv "01010" "荒川薬局 " "01020" "江東美容学校 " "01030" "墨田ビューティー " "01040" "江戸川美容サロン " "01050" "中央食品館 " "01060" "サロン・ド港 " "01070" "北栄養専門学校 " "01080" "文京薬剤 " "01090" "品川化粧品 " "01110" "大田総合研究所 "
csvファイルの場合、文字項目はダブルクォーテーション(”)で囲っているケースが多いので、これも取り除いてみましょう。
bash-5.1$ sed 's/"//g' tecsmp.csv | awk -F',' '{print $1,$3}' 01010 荒川薬局 01020 江東美容学校 01030 墨田ビューティー 01040 江戸川美容サロン 01050 中央食品館 01060 サロン・ド港 01070 北栄養専門学校 01080 文京薬剤 01090 品川化粧品 01110 大田総合研究所
まず、sedを使ってすべてのダブルクォーテーションを取り除き、その結果をパイプで awk に渡して、1番目と3番目の項目を出力しています。
apacheサーバーのアクセスログ解析
では、awkとその他のコマンドを使用して、apacheサーバーのアクセスログ解析を実行してみましょう。
: 172.23.1.17 0 - - [13/Dec/2024:17:22:38 +0900] "GET /xxxxx/js/YYYYYY.js?20241213172238 HTTP/1.1" 200 ..... :
上記は、IBM i のapacheサーバーの access.log ファイル内のログのサンプルです。ログ・ファイル内の各項目はスペースで区切られています。上記データをスペース毎に分割してみましょう。
No. | 値 |
---|---|
1 | 172.23.1.17 |
2 | 0 |
3 | – |
4 | – |
5 | [13/Dec/2024:17:22:38 |
6 | +0900] |
: | : |
では、awk 等を使用して時間ごとのアクセス数(ログ行数)を集計してみたいと思います。
まず、時間を含む部分(5 番目の「`[13/Dec/2024:17:22:38`」)を取り出します。
bash-5.1$ awk '{print $5}' access.log : [13/Dec/2024:08:58:48 [13/Dec/2024:08:58:50 [13/Dec/2024:08:58:50 [13/Dec/2024:08:58:50 [13/Dec/2024:08:58:50 [13/Dec/2024:08:58:50 [13/Dec/2024:08:58:50 :
次に、取り出した5番目の項目から時間を取り出します。コロンを区切り文字で指定すると、時間は2番目の項目として参照可能です。
bash-5.1$ awk '{print $5}' access.log | awk -F':' '{print $2}' : 08 08 08 08 08 08 08 :
上記の時間毎の件数をカウントして出力するには、uniq -c を使用します。uniqコマンドは、重複した行を取り除くものですが、-c を指定すると各行の前に出現回数(何行あったか)を結果の行の先頭に表示してくれます。時間毎に集計するので、念の為 uniq の前に sort しておきましょう。
:
8033 08
19431 09
17579 10
16385 11
12529 12
9234 13
7220 14
6656 15
6371 16
3950 17
:
これで、欲しい情報(時間ごとのアクセス回数)は取得できましたが、awkで表示形式を少し工夫してみましょう。
:
08時: 8033件
09時: 19431件
10時: 17579件
11時: 16385件
12時: 12529件
13時: 9234件
14時: 7220件
15時: 6656件
16時: 6371件
17時: 3950件
:
最後のawkで指定したprintfについて最後に解説します。
printfは出力のフォーマットを指定するコマンドです。指定する形式は以下の通りです。
printf("出力形式", 値1, 値2, ...)
出力形式には、書式指定子を含めて出力する値を細かに制御できます。今回使用した書式指定子と意味は以下の通りです。
書式指定子 | 出力 | サンプル | 意味 |
---|---|---|---|
%d | 整数 | %02d | 値を整数2桁として出力、2桁に満たない場合は前に 0 を埋める |
awkを使うと、テキスト・ファイルを効率的に処理および分析できるようになります。上記で紹介した利用方法はawkのごく入口に過ぎません。皆さんもぜひ想像力を働かせて、色々と試してみてください。
6. おわりに
今回は、「OSS ツール Part-2」と題して、テキスト処理に必須の手法である正規表現と、grep/egrep、sed および awk と、重要な3つのツールを取り上げました。これらのツールは、テキストデータの処理や解析において非常に強力な機能を提供します。
正規表現は、複雑な文字列パターンを単一の表現で表す手法です。特にテキストファイル内での検索条件として広く活用されており、様々なテキスト処理ツールと組み合わせることで、複雑な処理を簡潔なコマンドで実行することを可能にします。
grep/egrep、sed および awk は、正規表現を検索条件として指定することで、より高度で柔軟なテキスト処理が実現可能となります。各ツールは、パイプで連結することで、データの検索、抽出、加工、集計など、多様な処理を連続的に実行できます。特に大規模なapacheログファイルの解析など、複雑なテキスト処理タスクを効率的に実行できることを、使用例を通してご理解いただけたのではないでしょうか。
IBM i 環境においては、これらのツールの活用範囲がさらに広がることでしょう。IFS上のファイルだけでなく、データベースのレコードもテキストファイルに変換することで、より効果的なデータ処理が可能となるはずです。
IBM i の世界では、記録されているデータの検索は、QUERYなどの5250系ツールやSQLなどの使用が一般的です。また、データの集計が必要な場合はプログラミング言語を使用したデータ処理を行う傾向があるように思います。もちろん、このようなことのすべてが、今回紹介したテキスト処理ツールで代替できるわけではありません。しかし、工夫次第でより簡単かつ短時間で結果を得られる分野が僅かにでもあるならば、OSSツールを利用しない理由はないと思います。今回の記事が、「こんなこともできるようになるかもしれない」という皆さんのアイデアのきっかけになってくれればと思います。
次回は、OSS ツール Part-3 をお送りする予定です。お楽しみに!