こんにちは、Bash玄(ばっしゅげん)です。
Webサーバーのアクセスログ解析、CSVやTSVファイルのデータ抽出、システム監視コマンドの出力整形など、実務でテキストデータを扱う機会は日常茶飯事です。その際、 「特定カラムだけを抜き出したい」「条件に合致する行だけを抽出して合計や平均を瞬時に計算したい」 と思ったことはありませんか?
シェル上でこうしたデータ加工を手作業やExcelで行うのは時間がもったいありません。 「手作業は負け」 です。Linux環境において、列(フィールド)の切り出し、条件分岐、数値演算、グループ集計を最も高速かつ柔軟に完結できる最強のテキスト処理ツールが awk(オーク)コマンドです。
sed が「行単位の文字列置換」を得意とするのに対し、awk は 「列単位のデータ抽出」「条件判定」「数値集計・計算」 に無類の強さを誇ります。
この記事では、awk の基本構文や主要オプション、最重要の組み込み変数早見表から、実務でそのままコピペして使える列抽出・条件フィルタ・集計ワンライナー、そして現場で直面する落とし穴と対処法まで徹底解説します。
💡 【3秒で解決】実務頻出awkワンライナー逆引き早見表
実務で最もよく使われる awk の代表的ワンライナーをまとめました。まずはここからコピペして動かしてみてください:
・ 第1列と第3列を抽出 : awk '{print $1, $3}' access.log
・ カンマ区切り(CSV)の第2列を抽出 : awk -F',' '{print $2}' data.csv
・ 行の最終列を取得 : awk '{print $NF}' input.txt
・ ヘッダー行を除いて処理 : awk 'NR > 1 {print $1, $2}' data.tsv
・ 特定文字列を含む行だけ抽出 : awk '/ERROR/ {print $0}' /var/log/syslog
・ 数値条件で絞り込み(第3列が100以上) : awk '$3 >= 100' data.txt
・ 第2列の数値を合計 : awk '{sum += $2} END {print sum}' sales.txt
・ 特定列(IPなど)の出現回数を集計 : awk '{count[$1]++} END {for (k in count) print k, count[k]}' access.log
awkコマンドの基本構文と主要組み込み変数早見表
awk は単なるコマンドにとどまらず、テキスト処理に特化した完結したプログラミング言語です。しかし実務でワンライナーとして利用する分には、以下の基本骨格を押さえるだけで自在に扱えます。
基本書式: awk [オプション] ‘パターン { アクション }’ [ファイル]
awk の基本構文は 「パターン(条件)」 と 「アクション(処理内容)」 の組み合わせで成立しています。
# 基本構文
awk [オプション] 'パターン { アクション }' [入力ファイル]
# パイプライン経由での標準入力処理
コマンド | awk [オプション] 'パターン { アクション }'
この構文には以下の重要なルールがあります:
- パターンのみ指定した場合 : 条件に一致した行全体(
$0)がそのまま標準出力に出力されます(アクション{print}が省略された扱い)。 - アクションのみ指定した場合 : パターン(条件)を省略すると、すべての入力行に対して波括弧
{ ... }内のアクションが実行されます。 - シングルクォートで囲む : スクリプト全体をシングルクォート
'...'で囲むのが鉄則です。ダブルクォートで囲むと、Bashが$1や$NFをシェル変数と誤認して事前に展開してしまいます。
最重要の組み込み変数($0, $1〜$N, NR, NF, FS, OFS)一覧表
awk には、処理中の行やフィールド情報を自動で保持する便利な「組み込み変数」が用意されています。実務で必ず使う主要な組み込み変数を一覧表で整理しました。
| 組み込み変数 | 意味・役割 | 実務での主な用途・実行例 |
|---|---|---|
$0 | 入力された現在の行全体 | 行全体を丸ごと出力する(awk '{print $0}') |
$1, $2, ... $N | 第1列、第2列、第N列のフィールド値 | 特定カラムの抜き出し(awk '{print $1, $3}') |
NF | 現在の行の総フィールド数(Number of Fields) | 最終列の取得($NF)やカラム数チェック(NF == 5) |
NR | 現在までに読み込んだ通算行番号(Number of Records) | ヘッダー除外(NR > 1)や特定行の抽出(NR == 10) |
FNR | 現在読み込んでいるファイル内での行番号 | 複数ファイル処理時に各ファイルの先頭行を判別する |
FS | 入力フィールドの区切り文字(Field Separator / 既定: 空白) | オプション -F',' や BEGIN {FS=","} で設定 |
OFS | 出力フィールドの区切り文字(既定: 半角スペース) | カンマ区切りで出力する(BEGIN {OFS=","}) |
RS | 入力レコードの区切り文字(既定: 改行 n) | 空行区切りの複数行ブロックを1レコードとして処理する |
ORS | 出力レコードの区切り文字(既定: 改行 n) | 出力時の改行コード制御 |
FILENAME | 現在処理中の入力ファイル名 | 複数ファイルを一括処理する際のファイル名出力 |
主要コマンドラインオプション早見表(-F, -v, -f)
awk コマンド実行時に指定できる代表的なオプションは以下の3つです。実務の99%はこの3つの理解でカバーできます。
| オプション | 機能・説明 | 実用例 |
|---|---|---|
-F <文字> | 入力の区切り文字(FS)を指定する | awk -F',' '{print $1}' data.csv |
-v <変数=値> | Bash側の変数や任意の値をawk内部の変数として渡す | awk -v threshold=100 '$2 > threshold' file.txt |
-f <スクリプトファイル> | 外部ファイルに保存したawkスクリプトを読み込んで実行する | awk -f parse_log.awk access.log |
実務で最もよく使う列抽出パターン
awk を使う動機の第1位は「テキストやログから特定列を瞬時に切り出したい」というケースです。代表的な列抽出の実践パターンを見ていきましょう。
特定の列だけを抽出して表示する(print $1, $2)
スペース区切りのテキストから指定した列を抜き出す最も基本的なパターンです。print の後にカンマ(,)で区切って列変数を並べると、デフォルトでは半角スペース区切りで出力されます。
# サンプルデータの確認(プロセス一覧)
ps aux | head -n 5
# 第1列(USER)と第2列(PID)と第11列(COMMAND)を抽出
ps aux | awk '{print $1, $2, $11}'
# カンマなしで並べると連結されて出力される
ps aux | awk '{print $1 ":" $2}'
💡 カンマの有無による出力の違いに注意
print $1, $2 のようにカンマを入れると OFS(デフォルトは半角スペース)で区切られます。一方、print $1 $2 のようにカンマを省くと空白なしで文字列結合されます。
区切り文字(カンマ・タブなど)を指定する(-F オプション)
デフォルトの awk は「1つ以上の連続する空白文字(半角スペースまたはタブ)」を区切り文字として扱います。CSV(カンマ区切り)やTSV(タブ区切り)、コロン区切り(/etc/passwd など)を処理する場合は -F オプションを指定します。
# 1. カンマ区切り(CSV)の第1列と第3列を抽出
awk -F',' '{print $1, $3}' users.csv
# 2. タブ区切り(TSV)の抽出(-F't' を指定)
awk -F't' '{print $1, $2}' records.tsv
# 3. /etc/passwd からユーザー名(第1列)とログインシェル(第7列)を抽出
awk -F':' '{print $1, $7}' /etc/passwd
# 4. 複数の区切り文字を正規表現で指定(カンマまたはコロン)
awk -F'[,:]' '{print $1, $2}' mixed_data.txt
最終列を取得する(print $NF)
行ごとに列数が異なる可変長データや、パスの末尾ファイル名を取得したい場合、 「最終列(最後のカラム)」 をピンポイントで取得できる $NF が極めて役立ちます。
# 最終列(NFが示す番号のカラム)を出力
awk '{print $NF}' access.log
# 最後から2番目の列を取得
awk '{print $(NF-1)}' access.log
# 列数(フィールド数)と最終列を同時に表示
awk '{print "カラム数:" NF, "末尾:" $NF}' access.log
出力時の区切り文字を変更する(OFS=”,”)
スペース区切りの入力データをCSV(カンマ区切り)やTSV(タブ区切り)に変換して出力したい場合は、出力フィールド区切り文字 OFS(Output Field Separator)を設定します。
# 1. 出力をカンマ区切り(CSV化)に変更
awk 'BEGIN {OFS=","} {print $1, $2, $3}' data.txt
# 2. 出力をタブ区切りに変更
awk -v OFS='t' '{print $1, $2, $3}' data.txt
# 3. 出力をコロン区切りに変更
awk -v OFS=':' '{print $1, $2, $NF}' data.txt
フォーマットを指定して綺麗に出力する(printf 桁揃え)
print は自動で改行を付与しますが、C言語風の printf を使うことで、桁揃えやゼロ埋め、小数点以下の桁数指定など、見やすいレポート形式に出力できます。
# 文字列を左揃え15桁、数値を右揃え8桁で出力
awk '{printf "%-15s %8dn", $1, $2}' scores.txt
# 小数点第2位までフォーマットして出力
awk '{printf "平均点: %-10s %.2fn", $1, $2}' results.txt
条件分岐とテキストフィルタリング
awk はパターン(条件式)を記述することで、grep コマンドのような行フィルタリングと列抽出を1つのコマンドで同時に実行できます。別個に grep をパイプで繋ぐ必要がなくなるため、処理速度も向上します。
特定文字列を含む行だけを処理する(/ERROR/ {print $0})
スラッシュ /パターン/ で囲むことで、正規表現マッチングを行えます。特定列に対してのみマッチさせたい場合は ~(マッチ)や !~(非マッチ)演算子を使用します。
# 1. 行全体のどこかに「ERROR」を含む行だけを出力
awk '/ERROR/ {print $0}' /var/log/app.log
# アクションを省略しても同様(デフォルトで $0 が出力される)
awk '/ERROR/' /var/log/app.log
# 2. 第3列が「404」に完全一致、またはパターンを含む行を抽出
awk '$3 ~ /404/ {print $1, $3, $7}' access.log
# 3. 第3列が「200」以外の行を抽出(!~ は非マッチ)
awk '$3 !~ /200/ {print $1, $3, $7}' access.log
数値の比較条件でフィルタリングする($3 >= 100)
awk はフィールドの値を自動的に数値として解釈できるため、等号・不等号(==, !=, >, >=, <, <=)を使ったフィルタリングが直感的に行えます。
# 1. レスポンスタイム(第4列)が 500ミリ秒以上の行を抽出
awk '$4 >= 500 {print $1, $4, $NF}' access.log
# 2. HTTPステータスコード(第9列)が 500以上のサーバーエラー行を抽出
awk '$9 >= 500 {print $1, $9, $7}' access.log
# 3. 複数条件の組み合わせ(レスポンスが 500以上 かつ ステータスが 200)
awk '$4 >= 500 && $9 == 200 {print $1, $4, $9}' access.log
# 4. OR条件(ステータスが 500 または 503)
awk '$9 == 500 || $9 == 503 {print $1, $9, $7}' access.log
ヘッダー行を除外して2行目以降を処理する(NR > 1)
CSVファイルやコマンド出力(df -h や ps)の1行目には項目名(ヘッダー)が含まれていることがほとんどです。NR > 1 という条件を指定するだけで、ヘッダーをスキップしてデータ行のみを対象に処理できます。
# 1. df -h のヘッダー行を除外し、使用率が 80% 以上のパーティションを監視
df -h | awk 'NR > 1 && sub(/%/, "", $5) && $5 >= 80 {print $1, $5 "%", $6}'
# 2. CSVのヘッダーをスキップして2行目以降の第2列を出力
awk -F',' 'NR > 1 {print $2}' data.csv
# 3. 特定の行番号範囲(10行目〜20行目)だけを抽出
awk 'NR >= 10 && NR <= 20 {print NR, $0}' access.log
アクション内でのif-else文による柔軟な条件分岐
パターン部だけでなく、アクション { ... } の内部でも C言語やBashに似た if ... else 構文を記述できます。列の値に応じたラベル付けや条件別の集計に役立ちます。
# 応答速度に応じて "SLOW" / "FAST" のラベルを付与して出力
awk '{
if ($4 >= 500) {
status = "SLOW"
} else {
status = "OK"
}
print $1, $4 "ms", status
}' access.log
実務自動化・集計ワンライナー集
awk の真骨頂は 「BEGIN / END ブロック」 と 「連想配列」 による本格的なデータ集計です。重いスクリプトを書かなくても、シェル上で1行叩くだけでログ解析や集計作業が一瞬で完了します。
特定列の数値を合計・平均計算する(BEGIN / END ブロック)
BEGIN { ... } は全行の処理が始まる前に1度だけ実行され、END { ... } は全入力行を読み終えた後に1度だけ実行されます。各行で変数に加算し、END ブロックで結果を出力します。
# 1. 第2列の数値を合計して出力
awk '{sum += $2} END {print "合計:", sum}' sales.txt
# 2. 合計値と平均値を同時に計算(NRで行数カウント)
awk 'NR > 1 {sum += $3; count++} END {
if (count > 0) {
printf "データ件数: %d件n合計: %dn平均: %.2fn", count, sum, sum / count
}
}' scores.csv
# 3. ファイルサイズ(ls -l の第5列)の総合計バイト数を計算
ls -l | awk 'NR > 1 {total += $5} END {print "合計容量:", total, "bytes"}'
最大値・最小値を特定するワンライナー
ログの中から最大レスポンスタイムや最大アクセス数を特定するのも awk なら簡単です。
# 第2列の最大値と最小値を算出
awk 'NR == 1 {max = min = $2}
$2 > max {max = $2}
$2 < min {min = $2}
END {print "MAX:", max, "MIN:", min}' data.txt
ログ内のIPアドレスやステータスコードの出現回数を集計する(連想配列)
awk の配列はキーに文字列を使える 「連想配列」 です。キーごとの出現回数をカウントする count[$1]++ は、Webアクセスログ解析で最も多用される鉄板パターンです。
# 1. アクセスログのクライアントIP(第1列)の出現回数を集計し、上位10件を表示
awk '{count[$1]++} END {
for (ip in count) {
print count[ip], ip
}
}' access.log | sort -rn | head -n 10
# 2. HTTPステータスコード(第9列)別の件数を集計
awk '{status[$9]++} END {
for (code in status) {
printf "HTTP %s: %d件n", code, status[code]
}
}' access.log | sort -k2 -rn
重複行を高速に除外・ユニーク化する(!seen[$0]++)
通常、重複削除を行う場合は sort | uniq を使いますが、sort を挟むと元の行順序が崩れてしまいます。awk の !seen[$0]++ を使えば、 元の出現順序を維持したまま一瞬で重複行を排除 できます。
# 行全体の重複を削除(元の行順序を維持)
awk '!seen[$0]++' input.txt
# 特定列(例: 第1列のメールアドレス)で重複を削除
awk '!seen[$1]++' users.txt
💡 なぜ !seen[$0]++ で重複が消えるのか?
seen[$0] の初期値は 0(未定義)です。初めて登場した行は 0 なので、NOT演算子 ! により真(1)と評価されて出力されます。その直後にインクリメント ++ で値が 1 になります。2回目以降に同じ行が登場すると 1 なので !1 は偽(0)となり出力されません。非常に美しいシェルハックです。
実務で差がつく落とし穴・トラブルシューティング
現場で awk を使っていると、「意図した列が取れない」「計算結果がおかしい」というトラブルに遭遇することがあります。初心者がハマりやすい4大落とし穴と解決策を押さえておきましょう。
1. スペース区切りとタブ区切りの挙動差(デフォルトFSの空白圧縮挙動)
デフォルトの FS(区切り文字未指定)では、 「連続する複数の半角スペースやタブ」をまとめて1つの区切り として扱います。しかし -F' ' のように明示的にスペース1文字を指定すると、「連続する空白ごとに空のフィールド」が生成されてしまい、列番号がズレます。
# 連続スペースを含むデータ
# "apple banana orange"
# 正しい挙動(デフォルトの空白圧縮): $2 は "banana"
awk '{print $2}' file.txt
# 意図しない挙動(スペース1個単位で分割): $2 が空文字文字になる!
awk -F' ' '{print $2}' file.txt
連続空白を含む通常テキストを扱うときは、安易に -F' ' を指定せず、デフォルトのまま実行するのが鉄則です。
2. ダブルクォートで囲まれたCSV(クォート内カンマ)のパース限界
awk -F',' はシンプルで高速ですが、CSVのデータ値の中にカンマが含まれている場合(例: "Tokyo, Japan",100)、そこも区切り文字と誤認してしまいます。
💡 複雑なCSVは専用ツールの利用を推奨
値内部にカンマや改行を含む厳密なRFC 4180準拠CSVを処理する場合、awk 単体でのパースは不具合の温床になります。その場合は Python の csv モジュールや、CLIツール csvkit(csvcut)、q コマンドの併用を強く推奨します。
3. Bash変数をawk内に安全に渡す方法(-v オプション vs クォート囲み)
シェルスクリプト内で定義した変数を awk の内部で使いたい場合、無理やりクォートを切って埋め込むのはエスケープ事故やコマンドインジェクションの原因になります。必ず -v オプションを使いましょう。
TARGET_IP="192.168.1.10"
# ❌ 危険・読みにくい(クォートの継ぎ接ぎ)
awk '$1 == "'"$TARGET_IP"'" {print $0}' access.log
# ⭕ 安全・推奨(-v オプションで渡す)
awk -v target="$TARGET_IP" '$1 == target {print $0}' access.log
4. Windows改行コード(CRLF / r)による意図しない文字列乱れ
Windows環境で作成されたCSVやログファイルをLinux上の awk で処理すると、行末に復帰文字(r)が残るため、最終列 $NF を出力した際に画面表示が上書きされて崩れる現象が発生します。
# 症状: print $NF で先頭にカーソルが戻って文字が消えるように見える
# 対策1: dos2unix で事前に改行コードを LF に変換する
dos2unix input.csv
awk -F',' '{print $NF}' input.csv
# 対策2: awk 内部で r を一括置換して除去する
awk '{sub(/r$/, ""); print $NF}' input.csv
まとめ & 関連記事
awk は、Linuxサーバー運用やデータ分析の実務において、作業時間を劇的に削減してくれる超強力なコマンドです。最後に重要ポイントをおさらいしましょう。
- 基本書式 :
awk 'パターン { アクション }' ファイル(条件一致で行抽出し、波括弧内で列を処理) - 列抽出の基本 :
$0は行全体、$1〜$Nは各列、$NFは最終列。区切り文字は-F','で指定。 - 条件フィルタ :
$3 >= 100などの数値比較や/ERROR/の正規表現、NR > 1でヘッダー除外。 - 集計・自動化 :
BEGIN / ENDで合計・平均計算、連想配列count[$1]++で出現頻度集計。 - 変数の受け渡し : Bash変数は無理に文字列展開せず
-v var="$VAR"で安全に渡す。
テキスト加工は awk だけでなく、用途に応じて他のコマンドと組み合わせることでさらに真価を発揮します。ぜひ以下の関連記事も併せてチェックしてみてください。
関連コマンド&ピラー記事
- 【Linuxコマンド一覧・早見表】実務で頻出する主要コマンド・オプション逆引き完全ガイド(ピラー記事)
- 【Linux】sedコマンドの使い方と文字列置換完全ガイド|-i上書き・正規表現・行削除の逆引き早見表:行単位の置換や削除なら sed がベスト
- 【grepコマンド早見表】主要オプション一覧と実務で使える正規表現・検索パターン20選:行検索・キーワード抽出の必須コマンド
- BashでCSVを扱う方法まとめ|読み込み・ループ・配列処理の実践例:シェルでのCSVパース実践
- CSV加工はどこまでbashでできるのか:シェル加工の限界と使い分けの基準
- Bash cutコマンドの使い方徹底ガイド:データを効率的に分割・抽出する方法:単純な固定区切りなら cut も軽量高速
- findコマンドとxargsの組み合わせ術:ファイル検索から一括処理(exec)までの実務レシピ:複数ファイル横断のログ集計連携

コメント