awkコマンド:フィールドの出力と-F区切り文字、条件パターンの使い方

テスト環境RHEL系AlmaLinux 9.8rootアカウント2026-10-29 確認

awkコマンドは、テキストファイルを1行ずつ読み、空白や指定した区切り文字でフィールドに分け、条件に合う行のフィールドだけを選んで出力したり計算したりするLinuxのツールです。AlmaLinux 9サーバーでWebサーバーのアクセスログのサンプルファイルについて、接続元IPごとのリクエスト数とステータスコードごとの件数を、sortとパイプでつないだ1行のコマンドで集計したところ、12行のうち404応答3件はすべて同じIPからのwp-login.phpと.envへのリクエストでした。

以下の出力はAlmaLinux 9.8サーバーのrootアカウントで作業フォルダー内で実行した結果です。アクセスログ、アカウント一覧、販売記録、点数ファイルはテスト用に作ったサンプルファイルで、その中のIPアドレスとアカウント名も例の値です。

1️⃣ awkコマンドの基本的な使い方

  • ↪️ フィールド番号とprint

awkは読み込んだ行を空白で分け、先頭から$1、$2、$3のようにフィールド番号を付けます。名前、結果、点数が空白で分かれたscores.txtで確認しました。

cat scores.txt
alice   pass  82
bob     fail  47
carol   pass  91
dave    pass  68
erin    fail  55

シングルクォート内の波括弧が行ごとに実行する動作(アクション)で、printの後にフィールド番号をカンマで並べると、そのフィールドだけが出力されます。1番目と3番目のフィールドを出力すると、名前と点数だけが残ります。

awk '{print $1, $3}' scores.txt
alice 82
bob 47
carol 91
dave 68
erin 55

フィールドの間のカンマを外して$1 $3と書くと、2つの値が空白なしでつながります。printのカンマは出力区切り文字(標準は空白1つ)を入れる記号で、カンマなしで並べた値は1つの文字列につながります。

awk '{print $1 $3}' scores.txt
alice82
bob47
carol91
dave68
erin55

NRはこれまでに読んだ行の番号、NFはその行のフィールド数です。$NFは最後のフィールドを意味するので、行ごとにフィールド数が違っていても最後の欄を選べます。

awk '{print NR, NF, $NF}' scores.txt
1 3 82
2 3 47
3 3 91
4 3 68
5 3 55

Webサーバーのアクセスログも空白で分かれるので、同じ方法でフィールドを選びます。テスト用アクセスログの1行目のフィールド番号は次のとおりです。

フィールド1行目の値意味
$1192.0.2.10接続元IP
$4[29/Oct/2026:00:01:12リクエストの日時
$6"GETリクエストメソッド(前にダブルクォートが付く)
$7/index.htmlリクエストURL
$9200ステータスコード
$105120送信バイト数
$11"-"前のページ(Referer)
$12~$15"Mozilla/5.0 (X11; Linux x86_64)"ユーザーエージェント(空白ごとに分かれる)

接続元IP、リクエストURL、ステータスコードにあたる$1、$7、$9を選んで出力しました。

awk '{print $1, $7, $9}' access.log
192.0.2.10 /index.html 200
198.51.100.7 /about.html 200
192.0.2.10 /images/logo.png 200
203.0.113.25 /wp-login.php 404
203.0.113.25 /wp-login.php 404
192.0.2.10 /contact.html 200
198.51.100.7 /old-page.html 301
203.0.113.25 /.env 404
192.0.2.44 /index.html 200
198.51.100.7 /api/search?q=awk 500
192.0.2.44 /images/logo.png 304
192.0.2.10 /index.html 200

ユーザーエージェントのように値の中に空白がある欄は、複数のフィールドに分かれます。最初の3行のNFはすべて15で、$NFはユーザーエージェントの最後の断片です。

awk '{print NF, $NF}' access.log | head -3
15 x86_64)"
15 x86_64)"
15 x86_64)"

2️⃣ awk -Fで区切り文字を指定

  • ↪️ コロン、カンマ、正規表現の区切り文字

空白以外の記号で欄を分けたファイルは、-Fの後にその記号を書きます。/etc/passwdと同じ形で作ったアカウント一覧users.txtに-Fなしで$1を出力すると、ほとんどの行がそのまま表示されました。

awk '{print $1}' users.txt
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
nobody:x:65534:65534:Kernel
user:x:1000:1000:Example
deploy:x:1001:1001:Deploy
backup:x:1002:1002::/home/backup:/sbin/nologin

標準の区切り文字は空白なので、コロンだけでつながった行はフィールドが1つしかありません。説明欄に空白があるnobody、user、deployの行だけがその空白で切れます。

-F:でコロンを区切り文字に指定すると、アカウント名($1)とログインシェル($7)が分かれます。

awk -F: '{print $1, $7}' users.txt
root /bin/bash
bin /sbin/nologin
daemon /sbin/nologin
nobody /sbin/nologin
user /bin/bash
deploy /bin/bash
backup /sbin/nologin

カンマで区切ったCSVファイルは-F,で分けます。販売記録sales.csvの品目と数量を出力すると、1行目の見出し(item qty)も同じように分かれて一緒に表示されます。

awk -F, '{print $2, $3}' sales.csv
item qty
keyboard 2
mouse 5
monitor 1
keyboard 1
cable 10
mouse 2
monitor 2

-F,は、ダブルクォートで囲んだ欄の中にカンマが入ったCSVには向きません。品目欄にカンマが入ったquoted.csvは、クォート内のカンマでもフィールドが分かれ、2番目のフィールドに品目名の前半だけが残ります。

awk -F, '{print $2}' quoted.csv
item
"cable
keyboard

GNU Awkは、こうしたファイルにFPAT変数を使う方法を公式マニュアルで案内しています。区切り文字の代わりにフィールド1つの形を正規表現で書くと、クォートで囲んだ欄が1つのフィールドになります。

awk -v FPAT='([^,]*)|("[^"]*")' '{print $2}' quoted.csv
item
"cable, usb"
keyboard

区切り文字を複数使うには、角括弧の中に正規表現で書きます。コロンとスラッシュの両方を区切り文字に指定すると、$NFがログインシェルのパスの最後の名前(bash、nologin)になります。

awk -F'[:/]' '{print $1, $NF}' users.txt
root bash
bin nologin
daemon nologin
nobody nologin
user bash
deploy bash
backup nologin

3️⃣ awkコマンドの条件パターン

  • ↪️ 正規表現、比較演算、NR

フィールドを選んでもすべての行が出力されるので、必要な行だけを残すには波括弧の前に条件(パターン)を置きます。スラッシュの間に文字列を書くとその文字列を含む行だけを選び、アクションを省くと行全体が出力されます。

awk '/wp-login/' access.log
203.0.113.25 - - [29/Oct/2026:00:04:51 +0000] "POST /wp-login.php HTTP/1.1" 404 196 "-" "Mozilla/5.0 (X11; Linux x86_64)"
203.0.113.25 - - [29/Oct/2026:00:04:52 +0000] "POST /wp-login.php HTTP/1.1" 404 196 "-" "Mozilla/5.0 (X11; Linux x86_64)"

行を選ぶ結果は grepコマンドと同じで、awkは選んだ行からフィールドまでそのまま選んで出力できます。grepで選んだ同じ2行をパイプでawkに渡すと、IPとステータスコードだけが残ります。

grep wp-login access.log | awk '{print $1, $9}'
203.0.113.25 404
203.0.113.25 404

フィールドの値は==、!=、>、>=、<、<=で比較します。ステータスコード($9)が404の行だけを選び、IPとリクエストURLを出力しました。

awk '$9 == 404 {print $1, $7}' access.log
203.0.113.25 /wp-login.php
203.0.113.25 /wp-login.php
203.0.113.25 /.env

文字列と比較するときは、値をダブルクォートで囲みます。2番目のフィールドがpassの名前だけを選びました。

awk '$2 == "pass" {print $1}' scores.txt
alice
carol
dave

数値の比較は、アカウント番号(UID)のように範囲で選ぶときに使います。UIDが1000以上のアカウントを選ぶと、一般アカウントと一緒にUIDが65534のnobodyも含まれます。

awk -F: '$3 >= 1000 {print $1, $3}' users.txt
nobody 65534
user 1000
deploy 1001
backup 1002

2つの条件は&&で結びます。65534未満の条件を加えるとnobodyが外れ、一般アカウント3つとホームフォルダーだけが残ります。

awk -F: '$3 >= 1000 && $3 < 65534 {print $1, $6}' users.txt
user /home/user
deploy /home/deploy
backup /home/backup

[~]はフィールドが正規表現に一致するかどうか、[!~]は一致しないかどうかを確認します。ログインシェル($7)がbashで終わるアカウントと、nologinではないアカウントをそれぞれ選ぶと、同じ3つのアカウントが表示されます。

awk -F: '$7 ~ /bash$/ {print $1}' users.txt
root
user
deploy
awk -F: '$7 !~ /nologin/ {print $1, $7}' users.txt
root /bin/bash
user /bin/bash
deploy /bin/bash

NRの条件は行番号で選びます。NR > 1でCSVの見出し行を外すと、販売記録だけが残ります。

awk -F, 'NR > 1 {print $2, $3}' sales.csv
keyboard 2
mouse 5
monitor 1
keyboard 1
cable 10
mouse 2
monitor 2

ほかのコマンドの出力もパイプで渡して同じように分けます。ls -lの出力からファイルサイズ($5)と名前($9)を選ぶと、1行目が空行になりました。ls -lの1行目[total 24]には5番目と9番目のフィールドがないためです。

ls -l | awk '{print $5, $9}'

1567 access.log
79 quoted.csv
207 sales.csv
85 scores-copy.txt
85 scores.txt
315 users.txt

NR > 1を付けると1行目が外れ、ファイルサイズと名前だけが残ります。

ls -l | awk 'NR > 1 {print $5, $9}'
1567 access.log
79 quoted.csv
207 sales.csv
85 scores-copy.txt
85 scores.txt
315 users.txt

4️⃣ awkのBEGIN、ENDと合計、平均

  • ↪️ 変数とprintfの出力形式

BEGINブロックは最初の行を読む前に1回、ENDブロックは最後の行を処理した後に1回実行されます。合計は行ごとに変数に足しておき、ENDで出力します。下の図は、販売記録の金額合計を求めるときにawkが行ごとにたどる順序です。

🔁 awkがsales.csvを1行ずつ処理する順序
解説ダイアグラム
🔁 処理の順序
$ awk -F, 'NR > 1 {t += $3 * $4} END {printf "total %.2f\n", t}' sales.csv
① 読んだ行NR = 1date,item,qty,priceNR = 22026-10-01,keyboard,2,45.00NR = 32026-10-01,mouse,5,19.90NR = 82026-10-04,monitor,2,189.00これ以上読む行なし8行目が最後の行です。
② フィールドの分割 (-F,)$1date$2item$3qty$4price見出し行も同じように分けます。$12026-10-01$2keyboard$32$445.00$3 数量、$4 単価$12026-10-01$2mouse$35$419.90$3 数量、$4 単価$12026-10-04$2monitor$32$4189.00$3 数量、$4 単価分ける行なしENDブロックは行を読みません。
③ パターンとアクションNR > 1 偽アクションを飛ばします。t = (空の値)NR > 1 真t += 2 * 45.00t = 90NR > 1 真t += 5 * 19.90t = 189.5NR > 1 真t += 2 * 189.00t = 886.3END実行total 886.30printfがtを小数第2位まで出力します。
1行目は見出し行なのでNR > 1が偽になり、アクションなしで次の行に進みます。
2行目からはパターンが真になり、数量と単価を掛けた90が変数tに足されます。
変数tは行が変わっても値を保つので、3行目までの合計は189.5です。
4〜7行目も同じ順序で処理され、最後の8行目までの合計tは886.3です。
最後の行を処理した後にENDブロックが1回実行され、printfがtotal 886.30を出力します。

アクセスログの送信バイト数($10)をすべて足した結果は41298バイトでした。初めて使う変数は空の値(数値としては0)から始まるので、別に0を入れなくてもかまいません。

awk '{sum += $10} END {print sum}' access.log
41298

合計をENDではなく行ごとに実行するアクションで出力すると、行を読むたびにその時点までの合計が表示されます。数量と単価を掛けてtに足すたびに行番号とtを一緒に出力すると、見出しを除いた7行が表示され、最後の行の886.3が全体の合計です。

awk -F, 'NR > 1 {t += $3 * $4; print NR, t}' sales.csv
2 90
3 189.5
4 378.5
5 423.5
6 468.5
7 508.3
8 886.3

平均はENDで合計を行数で割って求めます。5つの点数の合計は343、平均は68.6でした。

awk '{total += $3; n++} END {print total, n, total / n}' scores.txt
343 5 68.6

BEGINは結果の上に見出し行を1回出力するときに使います。品目と金額(数量 × 単価)の上にitem totalの行が1回だけ表示されます。

awk -F, 'BEGIN {print "item", "total"} NR > 1 {print $2, $3 * $4}' sales.csv
item total
keyboard 90
mouse 99.5
monitor 189
keyboard 45
cable 45
mouse 39.8
monitor 378

出力区切り文字はBEGINでOFS変数を使って変えます。OFSをカンマにすると、printのカンマの位置に空白ではなくカンマが入り、アカウント一覧がCSVの形で出力されます。

awk 'BEGIN {FS = ":"; OFS = ","} {print $1, $3, $7}' users.txt
root,0,/bin/bash
bin,1,/sbin/nologin
daemon,2,/sbin/nologin
nobody,65534,/sbin/nologin
user,1000,/bin/bash
deploy,1001,/bin/bash
backup,1002,/sbin/nologin

桁をそろえるにはprintfを使います。%-10sは10桁左寄せの文字列、%3dは3桁の整数、%8.2fは小数第2位まで8桁にそろえた数値です。

awk -F, 'NR > 1 {printf "%-10s %8.2f\n", $2, $3, $3 * $4}' sales.csv
keyboard     2    90.00
mouse        5    99.50
monitor      1   189.00
keyboard     1    45.00
cable       10    45.00
mouse        2    39.80
monitor      2   378.00

printfはprintと違って改行しないので、形式の最後に[\n]を書きます。[\n]なしで実行すると、品目名が1行につながります。

awk -F, 'NR > 1 {printf "%s ", $2}' sales.csv
keyboard mouse monitor keyboard cable mouse monitor

5️⃣ awkコマンドのログ分析とオプション

  • ↪️ 接続元IPとステータスコードの集計、オプションのまとめ

🔲 ログ分析

変数1つに足す方法では合計が1つしか出ないので、接続元IPごと、ステータスコードごとに分けて数えるには配列に値を集めます。n[$1]++はIPをキーにした配列の要素に1ずつ足し、ENDのfor (ip in n)が集めた要素を1つずつ取り出します。

awk '{n[$1]++} END {for (ip in n) print n[ip], ip}' access.log | sort -rn
4 192.0.2.10
3 203.0.113.25
3 198.51.100.7
2 192.0.2.44

for (ip in n)が取り出す順序は決まっていないため(GNU Awkマニュアルによる)、sort -rnでリクエスト数の多い順に並べました。192.0.2.10が4件で最も多くなりました。

ステータスコードごとの件数も同じ方法で数えます。404が3件、500が1件でした。

awk '{n[$9]++} END {for (s in n) print s, n[s]}' access.log | sort
200 6
301 1
304 1
404 3
500 1

ステータスコードだけを取り出してsortとuniq -cに渡しても、同じ件数が出ます。

awk '{print $9}' access.log | sort | uniq -c
      6 200
      1 301
      1 304
      3 404
      1 500

エラー応答だけを見るには、ステータスコードに条件を付けます。400以上の行は、wp-login.phpと.envを探すリクエスト3件と500エラー1件でした。

awk '$9 >= 400 {print $9, $1, $7}' access.log
404 203.0.113.25 /wp-login.php
404 203.0.113.25 /wp-login.php
404 203.0.113.25 /.env
500 198.51.100.7 /api/search?q=awk

IPごとの送信バイト数の合計も配列に足して求めます。192.0.2.10が31340バイトで最も多く受け取りました。

awk '{b[$1] += $10} END {for (ip in b) print b[ip], ip}' access.log | sort -rn
31340 192.0.2.10
5120 192.0.2.44
4250 198.51.100.7
588 203.0.113.25

時刻フィールド($4)は文字列として比較できます。日付と形式が同じ行どうしでは文字列の順序が時刻の順序と同じなので、00:05から00:10の前までのリクエスト4件だけを選びました。

awk '$4 >= "[29/Oct/2026:00:05" && $4 < "[29/Oct/2026:00:10" {print $4, $7}' access.log
[29/Oct/2026:00:05:30 /contact.html
[29/Oct/2026:00:07:02 /old-page.html
[29/Oct/2026:00:07:15 /.env
[29/Oct/2026:00:09:48 /index.html

🔲 awkコマンドのオプション

📌 主なオプション: この記事で実行した結果

オプション作業この記事で確認した結果
-F fs入力フィールドの区切り文字を指定-F:でusers.txtのアカウント名($1)とログインシェル($7)が分かれる、-F,でsales.csvの品目と数量が見出し(item qty)と一緒に表示される
-v var=valプログラムの実行前に変数に値を指定シェル変数minを-v min=$minで渡すと送信バイト数が1000以上の6行を出力、-v FPAT=…でquoted.csvのクォートで囲んだ欄が1つのフィールドになる
-i inplace出力を画面の代わりに元のファイルに保存gawk -i inplaceの実行後、画面には何も表示されずscores.txtのコピーが名前と点数の2列に変わる、-v inplace::suffix=.bakを一緒に付けると変更前の内容が.bakファイルに残る
--versionバージョン情報を出力[awk --version]の1行目はGNU Awk 5のバージョン

📎 その他のオプション: 公式マニュアルより

オプション作業
-f program-fileAWKプログラムを最初のコマンドライン引数の代わりにファイルから読む、-fは複数回指定できる
-e program-textコマンドラインに書いた文字をAWKプログラムのソースとして使う、-fや--includeで読み込んだライブラリ関数と一緒に使える
-E file-fに似ているが最後に処理されるオプションで、コマンドラインの変数代入を防ぐ、#!スクリプト用
--traditional互換モードで実行し、GNU拡張機能を認識しない
--posix互換モードを有効にして制限を加える、たとえばfunctionの同義語funcを認識しない
--lint疑わしい構文や別のAWK実装に移しにくい構文を警告する、--lint=fatalなら警告が致命的エラーになる
--dump-variablesグローバル変数の型と最終値を並べた一覧をファイルに出力する、ファイルを指定しなければawkvars.out

🔲 awkコマンドシミュレーター

下のシミュレーターでは、同じテストファイルにawkコマンドを直接入力できます。

awkコマンドシミュレーターGNU Awk 5
[root@server work]#
ブラウザー内でのみ動作する小さなawkインタープリターです。テストファイル4つは実測に使ったファイルと同じで、↑↓で前に入力したコマンドを呼び出せます。

🔢 FAQ & おすすめコンテンツ

シングルクォート内の$minはシェルが置き換えずそのままawkに渡すので、-vオプションで渡します。シェル変数minに1000を入れて-v min=$minで渡すと、送信バイト数が1000以上の6行が表示されます。awkプログラムをダブルクォートで囲むと、[print $1]の$1までシェルが先に空の値に置き換えるため、行全体が出力されます。

GNU Awkは-i inplaceを付けると、出力を画面の代わりに元のファイルに保存します。scores.txtのコピーに1番目と3番目のフィールドを出力するプログラムをgawk -i inplaceで実行すると、画面には何も表示されず、ファイルが名前と点数の2列に変わりました。元のファイルをそのまま上書きするので、-v inplace::suffix=.bakを一緒に付けると変更前の内容が.bakファイルに残ります。

AlmaLinux 9サーバーでは/usr/bin/awkはgawkを指すシンボリックリンクで、[awk --version]の1行目はGNU Awk 5のバージョンでした。このサーバーでは2つの名前が同じプログラムを実行します。-i inplaceやFPATのようなGNU Awkの機能を使う前には、[awk --version]でGNU Awkかどうかを確認します。

ℹ️ 提携について
当サイトのコンテンツにはアフィリエイトリンクが含まれています。訪問者がこのリンクを通じて商品やサービスを購入すると、当サイトは販売元から手数料を受け取ります。この過程において、購入者が支払う金額(イベント割引適用で金額が下がります↓)は上昇しません。掲載されている価格・割引・在庫情報は作成時点のものであり、実際とは異なる場合がありますので、ご購入前に販売元にて最終確認をお願いいたします。商品の選定および評価は独自の基準に基づいて行われており、手数料の支払いの有無は紹介順や評価内容に影響を与えません。

コメントを残す

メールアドレスが公開されることはありません。 ※ が付いている欄は必須項目です

人間であることを証明してください: 6   +   2   =