現場で使えるシェルスクリプト入門15:awkコマンドによる列抽出とデータ集計の基本

シェルスクリプト IT

前回の記事(入門14)では、スクリプト内でファイルを直接開くことなく、テキストの文字を自動で書き換えたり、不要な行を一括で消し去ったりできる便利な「sed」コマンドについて解説しました。

今回は、スペースやカンマで区切られたデータ(ログファイルやCSVなど)から、特定の「列(カラム)」だけを抜き出したり、数値を足し算して集計したり、条件によって出力を切り替えたりできる強力なコマンド「awk」の基本的な使い方を解説します。

シェルスクリプトにおけるテキスト処理能力を飛躍的に高める「awk」の基本パターンを習得しましょう!

awk コマンドとは?

awk(オーク)は、単なるテキスト編集コマンドではなく、テキスト処理に特化した簡易的な「プログラミング言語」としての機能を持っています。

前回の sed が「行」を対象にした文字置換や行削除を得意とするのに対し、awk はスペースやカンマ、コロンなどの区切り文字で整理されたデータの「列(フィールド)」を対象にした操作や、数値の計算・集計を得意とするのが特徴です。

特定の「列(フィールド)」を抽出する基本

awk の最も基本的な使い方は、読み込んだテキストから指定した列だけを抜き出して表示することです。

awk '{print $1, $3}' file.txt

awk はテキストを1行ずつ読み込み、空白(スペースやタブ)を区切り文字として自動的にデータを列に分解します。分解された各列には、以下の「フィールド変数」を使ってアクセスします。

  • $0 : 行全体(すべてのデータ)
  • $1 : 1列目のデータ
  • $2 : 2列目のデータ(以降、$3, $4… と続きます)

💡 区切り文字を変更する「-F」オプション

デフォルトでは空白が区切り文字ですが、CSVファイル(カンマ区切り)や、システム設定ファイル(コロン区切り)を処理したい場合は、-F オプションを使って区切り文字(デリミタ)を指定します。

# 1. カンマ(,)区切りのCSVファイルから1列目と3列目を抽出する
awk -F, '{print $1, $3}' data.csv

# 2. コロン(:)区切りのパスワードファイルからユーザー名(1列目)を抽出する
awk -F: '{print $1}' /etc/passwd

条件を指定して特定の行だけを処理する

awk では、特定の条件にマッチした行だけを対象に処理(表示など)を実行することができます。

# 3列目の数値が 100 以上の行だけ、1列目を表示する
awk '$3 >= 100 {print $1}' score.txt

{print ...} のブロックの手前に条件を書くことで、条件に合致した行だけに処理を限定できます。また、数値だけでなく「特定のキーワードが含まれる行」を指定することも可能です。

# 行内に「ERROR」というキーワードが含まれる行だけ、1列目と5列目を表示する
awk '/ERROR/ {print $1, $5}' app.log

さらに柔軟に!awkの内部で「if文」による条件分岐を行う

処理ブロック { } の内部に if 文を記述することで、プログラミング言語のように複雑な条件分岐を行うことができます。

# 基本構文:
# { if (条件) { 処理1 } else { 処理2 } }

具体的な例を見てみましょう。以下のような、名前とテストの点数が書かれた score.txt というファイルがあるとします。

Tanaka 85
Sato 55
Suzuki 90

このファイルに対して、2列目の点数が「80点以上なら合格(Pass)、それ以外なら不合格(Fail)」と判定して出力するには、以下のように実行します。

awk '{ if ($2 >= 80) { print $1, "Pass" } else { print $1, "Fail" } }' score.txt

実行結果:

Tanaka Pass
Sato Fail
Suzuki Pass

このように、ifelse を用いた柔軟な分岐ロジックをテキスト処理に直接組み込めるのが awk の強みです。

💡 複数条件の分岐:else if を使って3つ以上に分ける

条件が3つ以上になる場合は、else if を使って条件を繋げていきます。

# 1. 80点以上は「A」、60点以上80点未満は「B」、それ以外は「C」と判定して表示する
awk '{
    if ($2 >= 80) {
        print $1, "A"
    } else if ($2 >= 60) {
        print $1, "B"
    } else {
        print $1, "C"
    }
}' score.txt

実行結果:

Tanaka A
Sato C
Suzuki A

プログラムが長くなる場合は、上記のように複数行に改行して記述することも可能です。これにより、複雑な条件判定を伴うテキスト処理も1コマンドで完結させることができます。

実務の定番!簡単な数値の集計(合計・平均)

awk の中で定義した変数は、テキストを読み込む間、自動で値を保持し続けます。これを利用して、数値の合計や平均を一瞬で計算させることができます。

# 2列目の数値をすべて足し算し、最後に合計を表示する
awk '{sum += $2} END {print "合計点数: ", sum}' score.txt

実行結果:

合計点数:  230

ここで重要なのが END というキーワードです。通常、awk の処理(`{sum += $2}`)はテキストの「全行」に対して毎回繰り返し実行されますが、頭に END を付けたブロック(`END {print …}`)は、「すべての行の読み込みと処理が完全に終わった最後の1回だけ」実行されます。これによって、途中の計算結果を最後にまとめて報告することができます。

💡 awk内での「変数」の扱い方

上の集計例では、合計値を保持するために sum という変数を自作して使用しました。awk の中では、事前の宣言なしで自由に独自の変数を定義できますが、シェルスクリプトの変数とは異なる重要な特徴があります。

それは、「awk内で定義した変数には $(ドルマーク)を付けない」という点です。

  • シェルスクリプトの変数 ➔ $var のように $ を付ける
  • awk内部の変数 ➔ sumcount のように $ を付けない

awk の世界において、$1$2 などのように $ を付ける記述は「列(カラム)」を指定する特別な意味を持っています。そのため、独自の変数(例:sumerror_count など)を扱う場合は、$ なしでそのまま記述します。この違いは初心者が非常につまずきやすい落とし穴なので、整理して覚えておきましょう。

💡 awkで使える便利な演算子まとめ

awk では、C言語やJavaなどのプログラミング言語とほぼ同様の便利な「演算子」が使用できます。集計や条件分岐でよく使う代表的なものを整理しておきましょう。

分類演算子意味・使用例
算術演算子+, -, *, /, %, ^四則演算、剰余(割り切れるかの判定)、べき乗を行います。
自己代入演算子+=, -=sum += $2 (既存の値に足し合わせる)のように数値を累計します。
インクリメント++, --count++(値を1増やす)のように件数をカウントアップします。
比較演算子==, !=, >, <, >=, <=値が等しいか、大小関係を判定します。
論理演算子&&, ||, !「かつ」「または」「否定」を組み合わせます(例:$2 >= 60 && $2 < 80)。
パターンマッチ~, !~$1 ~ /Tanaka/(1列目が指定の正規表現に「マッチする」または「マッチしない」)を判定します。
三項演算子条件 ? 真 : 偽条件分岐を1行で書きます。

特に、パターンマッチ演算子(~!~)は awk 特有の強力な機能で、「特定の列に対してだけ正規表現でマッチングを行う」という高度なフィルタリングを簡単に記述できます。

また、後半のスクリプト例に登場する (error_count ? error_count : 0) という記述は三項演算子を使用しており、「もし変数 error_count に値(1以上)が入っていればその値を返し、一度もカウントされず空のままであれば 0 を返す」という処理を1行で行っています。awk では、定義しただけで値が一度も加算されなかった変数は「空文字(表示されない)」となってしまうため、画面に綺麗に 0 件 と出力させるための実務的なテクニックです。

実務で役立つ!ログファイルのエラー集計スクリプト

最後に、実際のシェルスクリプトで awk を活用する実用的な例を紹介します。

このスクリプトは、Webサーバーのアクセスログ(access.log)を読み込み、ステータスコードが400以上のエラーリクエスト行を if 文で判定し、その合計エラー件数を出力するものです。

#!/bin/bash
set -euo pipefail

# access.log の中身の例(簡易版):
# 192.168.1.10 - [16/Jul/2026] "GET /index.html" 200
# 192.168.1.11 - [16/Jul/2026] "GET /admin" 403
# 192.168.1.12 - [16/Jul/2026] "POST /login" 500

log_file="access.log"

if [ ! -f "$log_file" ]; then
    echo "エラー: $log_file が存在しません。" >&2
    exit 1
fi

echo "アクセスログのエラーを集計中..."

# 6列目のステータスコードを判定し、400以上なら件数をカウントする
# すべて読み終わった後に合計件数を表示する
awk '
{
    status_code = $6
    if (status_code >= 400) {
        error_count++
    }
}
END {
    print "------------------------"
    print "合計エラー件数: " (error_count ? error_count : 0) " 件"
}
' "$log_file"

実行結果:

# ./test_awk1.sh 
アクセスログのエラーを集計中...
------------------------
合計エラー件数: 2 件

まとめ

awk を使いこなすことで、テキストファイルを表データのように扱い、特定の列の切り出しから if 文による判定、合計値の計算までを数行のコードで実現できます。

前回の sed と組み合わせることで、シェルスクリプトにおけるテキスト処理の幅は大きく広がります。まずは「printでの特定の列の取り出し」から、日々のログ調査などで試してみてください!

コメント

タイトルとURLをコピーしました