awkコマンド:フィールドの出力と-F区切り文字、条件パターンの使い方
awkコマンドは、テキストファイルを1行ずつ読み、空白や指定した区切り文字でフィールドに分け、条件に合う行のフィールドだけを選んで出力したり計算したりするLinuxのツールです。AlmaLinux 9サーバーでWebサーバーのアクセスログのサンプルファイルについて、接続元IPごとのリクエスト数とステータスコードごとの件数を、sortとパイプでつないだ1行のコマンドで集計したところ、12行のうち404応答3件はすべて同じIPからのwp-login.phpと.envへのリクエストでした。
以下の出力はAlmaLinux 9.8サーバーのrootアカウントで作業フォルダー内で実行した結果です。アクセスログ、アカウント一覧、販売記録、点数ファイルはテスト用に作ったサンプルファイルで、その中のIPアドレスとアカウント名も例の値です。
1️⃣ awkコマンドの基本的な使い方
awkは読み込んだ行を空白で分け、先頭から$1、$2、$3のようにフィールド番号を付けます。名前、結果、点数が空白で分かれたscores.txtで確認しました。
cat scores.txtalice pass 82
bob fail 47
carol pass 91
dave pass 68
erin fail 55シングルクォート内の波括弧が行ごとに実行する動作(アクション)で、printの後にフィールド番号をカンマで並べると、そのフィールドだけが出力されます。1番目と3番目のフィールドを出力すると、名前と点数だけが残ります。
awk '{print $1, $3}' scores.txtalice 82
bob 47
carol 91
dave 68
erin 55フィールドの間のカンマを外して$1 $3と書くと、2つの値が空白なしでつながります。printのカンマは出力区切り文字(標準は空白1つ)を入れる記号で、カンマなしで並べた値は1つの文字列につながります。
awk '{print $1 $3}' scores.txtalice82
bob47
carol91
dave68
erin55NRはこれまでに読んだ行の番号、NFはその行のフィールド数です。$NFは最後のフィールドを意味するので、行ごとにフィールド数が違っていても最後の欄を選べます。
awk '{print NR, NF, $NF}' scores.txt1 3 82
2 3 47
3 3 91
4 3 68
5 3 55Webサーバーのアクセスログも空白で分かれるので、同じ方法でフィールドを選びます。テスト用アクセスログの1行目のフィールド番号は次のとおりです。
| フィールド | 1行目の値 | 意味 |
|---|---|---|
| $1 | 192.0.2.10 | 接続元IP |
| $4 | [29/Oct/2026:00:01:12 | リクエストの日時 |
| $6 | "GET | リクエストメソッド(前にダブルクォートが付く) |
| $7 | /index.html | リクエストURL |
| $9 | 200 | ステータスコード |
| $10 | 5120 | 送信バイト数 |
| $11 | "-" | 前のページ(Referer) |
| $12~$15 | "Mozilla/5.0 (X11; Linux x86_64)" | ユーザーエージェント(空白ごとに分かれる) |
接続元IP、リクエストURL、ステータスコードにあたる$1、$7、$9を選んで出力しました。
awk '{print $1, $7, $9}' access.log192.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 -315 x86_64)"
15 x86_64)"
15 x86_64)"2️⃣ awk -Fで区切り文字を指定
空白以外の記号で欄を分けたファイルは、-Fの後にその記号を書きます。/etc/passwdと同じ形で作ったアカウント一覧users.txtに-Fなしで$1を出力すると、ほとんどの行がそのまま表示されました。
awk '{print $1}' users.txtroot: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.txtroot /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.csvitem 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.csvitem
"cable
keyboardGNU Awkは、こうしたファイルにFPAT変数を使う方法を公式マニュアルで案内しています。区切り文字の代わりにフィールド1つの形を正規表現で書くと、クォートで囲んだ欄が1つのフィールドになります。
awk -v FPAT='([^,]*)|("[^"]*")' '{print $2}' quoted.csvitem
"cable, usb"
keyboard区切り文字を複数使うには、角括弧の中に正規表現で書きます。コロンとスラッシュの両方を区切り文字に指定すると、$NFがログインシェルのパスの最後の名前(bash、nologin)になります。
awk -F'[:/]' '{print $1, $NF}' users.txtroot bash
bin nologin
daemon nologin
nobody nologin
user bash
deploy bash
backup nologin3️⃣ awkコマンドの条件パターン
フィールドを選んでもすべての行が出力されるので、必要な行だけを残すには波括弧の前に条件(パターン)を置きます。スラッシュの間に文字列を書くとその文字列を含む行だけを選び、アクションを省くと行全体が出力されます。
awk '/wp-login/' access.log203.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.log203.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.txtalice
carol
dave数値の比較は、アカウント番号(UID)のように範囲で選ぶときに使います。UIDが1000以上のアカウントを選ぶと、一般アカウントと一緒にUIDが65534のnobodyも含まれます。
awk -F: '$3 >= 1000 {print $1, $3}' users.txtnobody 65534
user 1000
deploy 1001
backup 10022つの条件は&&で結びます。65534未満の条件を加えるとnobodyが外れ、一般アカウント3つとホームフォルダーだけが残ります。
awk -F: '$3 >= 1000 && $3 < 65534 {print $1, $6}' users.txtuser /home/user
deploy /home/deploy
backup /home/backup[~]はフィールドが正規表現に一致するかどうか、[!~]は一致しないかどうかを確認します。ログインシェル($7)がbashで終わるアカウントと、nologinではないアカウントをそれぞれ選ぶと、同じ3つのアカウントが表示されます。
awk -F: '$7 ~ /bash$/ {print $1}' users.txtroot
user
deployawk -F: '$7 !~ /nologin/ {print $1, $7}' users.txtroot /bin/bash
user /bin/bash
deploy /bin/bashNRの条件は行番号で選びます。NR > 1でCSVの見出し行を外すと、販売記録だけが残ります。
awk -F, 'NR > 1 {print $2, $3}' sales.csvkeyboard 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.txtNR > 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.txt4️⃣ awkのBEGIN、ENDと合計、平均
BEGINブロックは最初の行を読む前に1回、ENDブロックは最後の行を処理した後に1回実行されます。合計は行ごとに変数に足しておき、ENDで出力します。下の図は、販売記録の金額合計を求めるときにawkが行ごとにたどる順序です。
$ awk -F, 'NR > 1 {t += $3 * $4} END {printf "total %.2f\n", t}' sales.csvアクセスログの送信バイト数($10)をすべて足した結果は41298バイトでした。初めて使う変数は空の値(数値としては0)から始まるので、別に0を入れなくてもかまいません。
awk '{sum += $10} END {print sum}' access.log41298合計をENDではなく行ごとに実行するアクションで出力すると、行を読むたびにその時点までの合計が表示されます。数量と単価を掛けてtに足すたびに行番号とtを一緒に出力すると、見出しを除いた7行が表示され、最後の行の886.3が全体の合計です。
awk -F, 'NR > 1 {t += $3 * $4; print NR, t}' sales.csv2 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.txt343 5 68.6BEGINは結果の上に見出し行を1回出力するときに使います。品目と金額(数量 × 単価)の上にitem totalの行が1回だけ表示されます。
awk -F, 'BEGIN {print "item", "total"} NR > 1 {print $2, $3 * $4}' sales.csvitem 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.txtroot,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.csvkeyboard 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.00printfはprintと違って改行しないので、形式の最後に[\n]を書きます。[\n]なしで実行すると、品目名が1行につながります。
awk -F, 'NR > 1 {printf "%s ", $2}' sales.csvkeyboard mouse monitor keyboard cable mouse monitor5️⃣ awkコマンドのログ分析とオプション
🔲 ログ分析
変数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 -rn4 192.0.2.10
3 203.0.113.25
3 198.51.100.7
2 192.0.2.44for (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 | sort200 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.log404 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=awkIPごとの送信バイト数の合計も配列に足して求めます。192.0.2.10が31340バイトで最も多く受け取りました。
awk '{b[$1] += $10} END {for (ip in b) print b[ip], ip}' access.log | sort -rn31340 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-file | AWKプログラムを最初のコマンドライン引数の代わりにファイルから読む、-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コマンドを直接入力できます。
🔢 FAQ & おすすめコンテンツ
ℹ️ 提携について
当サイトのコンテンツにはアフィリエイトリンクが含まれています。訪問者がこのリンクを通じて商品やサービスを購入すると、当サイトは販売元から手数料を受け取ります。この過程において、購入者が支払う金額(イベント割引適用で金額が下がります↓)は上昇しません。掲載されている価格・割引・在庫情報は作成時点のものであり、実際とは異なる場合がありますので、ご購入前に販売元にて最終確認をお願いいたします。商品の選定および評価は独自の基準に基づいて行われており、手数料の支払いの有無は紹介順や評価内容に影響を与えません。