awkのレコード、フィールド、カスタム区切り文字
入力と出力のフィールド区切り文字を制御し、CSV、TSV、ログ行を整った列に分割します。
「awkのレコード、フィールド、カスタム区切り文字」はCoddyKit上の無料DevOps Bootcampレッスンです。 これはレッスン1/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはDevOps Bootcamp学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 DevOps Bootcampコースには全4レッスンが含まれています。
awkのレコードとフィールドとは
awkは入力を1行ずつ読み取ります。各行をレコードと呼び、その行の中で空白によって区切られた各部分をフィールドと呼びます。
$0— 現在のレコード全体(行全体)$1— 最初のフィールド$2— 2番目のフィールド$NF— 最後のフィールド(NF= フィールド数)
デフォルトでは、awkは連続する空白(スペースまたはタブ)でフィールドを分割します。そのため、ログの解析、コマンド出力、空白区切りのデータにすぐ利用できます。
#!/usr/bin/env bash
# Show how awk sees records and fields
echo 'alice 30 engineer' | awk '{ print "Name:", $1, "Age:", $2, "Role:", $3 }'
# $NF is always the last field — regardless of how many there are
echo 'one two three four five' | awk '{ print "Last field:", $NF }'入力フィールド区切り文字: FS
組み込み変数FS(Field Separator)は、各レコードをフィールドに分割する方法をawkに伝えます。デフォルト値は単一のスペースで、空白分割モードになります。
FSは2つの方法で設定できます。
- コマンドラインで
-Fフラグを使う:awk -F':' BEGINブロック内で設定する:BEGIN { FS = ":" }
どちらも同じ動作です。長いスクリプトでは、設定をスクリプト内に置けるため、BEGINブロックを使う方法が推奨されます。コードが読みやすく、移植もしやすくなります。
#!/usr/bin/env bash
# Parse /etc/passwd using ':' as the field separator
# Field 1 = username, field 3 = UID, field 7 = shell
echo 'root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin' \
| awk -F':' '{ print $1, "UID="$3, "shell="$7 }'BEGINブロックでFSを設定する
ワンライナーより長いスクリプトでは、BEGINブロック内にFSを置くのが標準的です。BEGINブロックはawkが入力を読み取る前に実行されるため、最初のレコードから区切り文字が準備されています。
このパターンでは、複数の変数を一度に設定したり、コメントを追加したり、ロジックをスクリプトファイル内にまとめたりすることもできます。
実際のデータでよく使われる区切り文字は次のとおりです。
- コロン
:—/etc/passwd、/etc/shadow - タブ
\t— TSVエクスポート、コンパイラーの出力 - カンマ
,— CSVファイル(単純な形式、引用符付きフィールドなし) - パイプ
|— 一部のログ形式、データベースダンプ
#!/usr/bin/env bash
# awk script using BEGIN to set FS for TSV input
printf 'alice\t30\tengineer\nbob\t25\tdesigner\ncarol\t35\tmanager\n' \
| awk 'BEGIN { FS = "\t" } { printf "%-10s age=%-3s role=%s\n", $1, $2, $3 }'awkでCSVファイルを解析する
CSV(comma-separated values)は、シェル環境で最も一般的なデータ形式の1つです。FS=","を設定すると、awkはカンマを区切り文字として扱います。
重要な注意点: awkに組み込まれたCSV処理は、カンマを含む引用符付きフィールド(例: "Smith, John")には対応していません。引用符付きのカンマを含まない単純なCSVであれば問題なく動作します。RFC 4180に準拠した引用符付きフィールドを含むCSVには、適切なCSVパーサーまたはmiller/csvkitを使ってください。
実務でよくある作業は、特定の列を抽出し、値で行をフィルタリングすることです。FSを設定すれば、どちらもawkで簡単に実行できます。
#!/usr/bin/env bash
# Simple CSV: extract name and salary columns, filter salary > 50000
data='name,department,salary
alice,engineering,95000
bob,marketing,48000
carol,engineering,112000
dave,hr,45000'
echo "$data" | awk -F',' '
NR == 1 { next } # skip header row
$3 > 50000 { print $1, "earns", $3 }
'複数文字および正規表現のフィールド区切り文字
awkのFSは1文字に限らず、正規表現も指定できます。これは、フィールドが長さの異なる区切り文字で分けられている実際のログ形式で威力を発揮します。
正規表現による区切り文字の例:
FS = "[,;]"— カンマまたはセミコロンで分割FS = "[ \t]+"— 1つ以上のスペースまたはタブで分割(デフォルトと同じですが、明示的に指定)FS = "::"— 文字どおりの二重コロンで分割FS = "\\|"— パイプ文字で分割(エスケープが必要)
FSに正規表現を指定すると、awk(gawk)はそれをリテラル文字列ではなくパターンとして扱います。
#!/usr/bin/env bash
# Log lines with mixed delimiters: split on " | " or "::" or ","
# Here we split on one or more spaces or pipe characters
printf '2024-01-15 | ERROR | disk full | /dev/sda1\n2024-01-15 | INFO | startup ok | main\n' \
| awk -F' \\| ' '{ printf "date=%-12s level=%-6s msg=%s\n", $1, $2, $3 }'
# Split on multiple possible delimiters using character class
echo 'alpha,beta;gamma,delta;epsilon' \
| awk -F'[,;]' '{ for(i=1; i<=NF; i++) print i": "$i }'出力フィールド区切り文字: OFS
OFS(Output Field Separator)は、printでレコードを再構築するときに、フィールド間へawkが挿入する文字列を制御します。デフォルトは単一のスペースです。
重要なのは、OFSがフィールド間に挿入されるのは、printでカンマ構文を使った場合、またはフィールドに代入してawkが$0を再構築した場合だけだという点です。ソースコード内でスペースを使って連結しても、OFSは使われません。
print $1, $2, $3— カンマによってフィールド間にOFSが挿入されますprint $1 " " $2— 明示的なスペースなので、OFSは無視されます
よくあるパターンは、FS=","とOFS="\t"を設定して、CSVを読み込み、変換してTSVとして書き出すことです。
#!/usr/bin/env bash
# Convert CSV to TSV using FS and OFS
data='alice,30,engineer
bob,25,designer
carol,35,manager'
echo "$data" | awk 'BEGIN { FS=","; OFS="\t" } { print $1, $2, $3 }'
echo '---'
# OFS also applies when you modify a field — awk rebuilds $0 with OFS
echo 'alice,30,engineer' | awk 'BEGIN { FS=","; OFS=" | " } { $1=$1; print $0 }'OFSでawkに$0を再構築させる
微妙ですが重要なテクニックがあります。任意のフィールドに代入すると(フィールド自身を代入する$1=$1でも)、awkはすべてのフィールドをOFSで結合して$0を再構築します。
これは、すべてのフィールドを手動で出力せずにレコードの区切り文字を整形する、awkの慣用的な方法です。
FSを入力の区切り文字に設定しますOFSを目的の出力区切り文字に設定します$1=$1で再構築を発生させます$0を出力します
この方法はフィールド数に関係なく使え、$1, $2, $3, ...をハードコードせずに済みます。
#!/usr/bin/env bash
# Reformat a pipe-delimited file to comma-separated WITHOUT listing every field
printf 'alice|30|engineer|london\nbob|25|designer|paris\ncarol|35|manager|berlin\n' \
| awk 'BEGIN { FS="|"; OFS="," } { $1=$1; print $0 }'
# Useful when you don't know how many fields there are:
printf 'a|b|c|d|e|f|g\n1|2|3|4|5|6|7\n' \
| awk 'BEGIN { FS="|"; OFS="\t" } { $1=$1; print $0 }'レコード区切り: RS
FS がレコード内のフィールドを分割するのと同様に、RS(レコード区切り)はレコード同士を区切るものを定義します。デフォルトでは RS は改行であるため、awk は1行ずつ処理します。
RS を変更すると、複数行にまたがるレコードを柔軟に処理できるようになります。
RS=""— 段落モード: 空行でレコードを区切ります(フィールドは複数行にまたがることができます)RS=";"— セミコロンで分割します(SQLダンプに便利です)RS="\n"— 明示的な改行です(デフォルト)
段落モード(RS="")でも、複数行のレコード内でフィールドを分割するために FS が使われます。また、レコード内の改行も自動的にフィールド区切りとして扱われます。
#!/usr/bin/env bash
# Paragraph mode: each blank-line-separated block is one record
# Useful for processing structured text blocks (e.g., stanzas, config sections)
data='Name: Alice
Role: Engineer
City: London
Name: Bob
Role: Designer
City: Paris'
echo "$data" | awk 'BEGIN { RS=""; FS="\n" } {
print "Record", NR":"
for (i=1; i<=NF; i++) print " ", $i
print ""
}'出力レコード区切り: ORS
ORS(出力レコード区切り)は、各 print 文の後に出力されます。デフォルトは改行(\n)なので、すべての print は新しい行に出力されます。
ORS を変更すると、次のことができます。
- レコードを1行に連結する:
ORS=" " - 出力レコードの間に空行を追加する:
ORS="\n\n" - JSONやXMLの断片など、独自の出力形式を作成する
printf は ORS を使用しない点に注意してください。ORS が適用されるのは、単独の print 文だけです。書式を完全に制御する必要がある場合は、printf を使用してください。
#!/usr/bin/env bash
# Join all matching lines onto one line by setting ORS to a space
printf 'apple\nbanana\ncherry\ndate\n' | awk '{ ORS=" " } { print $0 }'
echo # final newline
# Add a blank line after every output record for readability
printf 'alice 30 engineer\nbob 25 designer\ncarol 35 manager\n' \
| awk 'BEGIN { ORS="\n\n" } { print $1, "is a", $3 }'実践例: Apacheアクセスログの解析
Apache/nginx のアクセスログは、空白で区切られたフィールドを持つよく知られた形式です。タイムスタンプなどの一部のフィールドには空白が含まれ、角括弧や引用符で囲まれています。そのため、awk で直接フィールド分割するのは簡単ではありません。
実践的な方法としては、構造を考慮した正規表現の FS を使用するか、正確な形式を把握したうえで位置によって特定のフィールドを抽出します。
Combined Log Format のフィールドは、おおむね次のとおりです。
- クライアントIP
- Ident(通常は
-) - 認証ユーザー(通常は
-) - タイムスタンプ(角括弧内にあり、1つのトークンとして扱われます)
- リクエストのメソッド+パス+プロトコル(引用符内)
- HTTPステータスコード
- レスポンスバイト数
#!/usr/bin/env bash
# Parse a simulated Apache Combined Log Format line
# Extract: IP, status code, bytes, and request path
logs='192.168.1.10 - alice [15/Jan/2024:10:23:45 +0000] "GET /api/users HTTP/1.1" 200 1523
10.0.0.5 - - [15/Jan/2024:10:23:46 +0000] "POST /login HTTP/1.1" 401 89
172.16.0.3 - bob [15/Jan/2024:10:23:47 +0000] "GET /dashboard HTTP/1.1" 200 8741'
echo "$logs" | awk '{
ip = $1
status = $9
bytes = $10
# Extract the request path from the quoted string in field 7
split($7, parts, "/")
path = "/" parts[2]
printf "ip=%-15s status=%s bytes=%-6s endpoint=%s\n", ip, status, bytes, path
}'パイプラインで FS、OFS、RS、ORS を組み合わせる
実際のシェル開発では、これらの区切り文字を単独で使うことはほとんどありません。一般的なパイプラインのパターンでは、パイプラインの途中で awk を形式変換ツールとして使い、ある構造化形式を別の形式に変換します。
区切り文字を組み合わせる際の重要なポイント:
- 形式を変換するときは、
BEGIN内で必ずFSとOFSを同時に設定します - すべてのフィールドを列挙せずに再フォーマットしたい場合は、
$1=$1を使って$0の再構築を発生させます - レコードが実際に複数行にまたがる場合にのみ
RSを変更します - 出力レコード間の空白を制御するには
ORSを使います - 正確な書式設定には
printfを優先し、ORSによる自動終端が便利な場合はprintを使います
#!/usr/bin/env bash
# Full pipeline: read a colon-delimited file,
# filter rows where field 3 (UID) >= 1000 (real users),
# then output as tab-separated: username, UID, home dir
echo 'root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
alice:x:1001:1001:Alice:/home/alice:/bin/bash
bob:x:1002:1002:Bob:/home/bob:/bin/zsh
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin' \
| awk 'BEGIN { FS=":"; OFS="\t" } $3 >= 1000 { print $1, $3, $6 }'理解度チェック: OFS とフィールドの再構築
awk でフィールドに代入したとき、OFS がどのように作用するかを確認しましょう。
レッスンのまとめ: レコード、フィールド、区切り文字
これで、awk が構造化データを読み書きする方法を完全に制御できるようになりました。4つの区切り文字変数をまとめます。
FS— 入力フィールド区切り。-FまたはBEGINで設定します。文字、文字列、正規表現を指定できます。デフォルトは空白です。OFS— 出力フィールド区切り。print $1, $2の呼び出しでフィールド間に挿入されます。また、フィールドへの代入後に awk が$0を再構築するときにも使われます。デフォルトは1つの空白です。RS— 入力レコード区切り。レコード(行)を区切るものを定義します。デフォルトは改行です。空文字列に設定すると段落モードになります。ORS— 出力レコード区切り。すべてのprintの後に追加されます。デフォルトは改行です。行を連結したり、空白を追加したりするために変更できます。
覚えておくべき最も重要なパターンは、BEGIN で FS と OFS の両方を設定し、フィールド位置をハードコーディングせずにすべてのフィールドの区切り文字を再フォーマットしたいときは $1=$1 で $0 を再構築することです。このパターンは列数に関係なく使えるため、awk ベースの形式変換パイプラインの基盤となります。
よくある質問
「awkのレコード、フィールド、カスタム区切り文字」レッスンは無料ですか?
はい。「awkのレコード、フィールド、カスタム区切り文字」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、DevOps Bootcampコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 DevOps Bootcampコースには全4レッスンが含まれています。
「awkのレコード、フィールド、カスタム区切り文字」で何を学びますか?
入力と出力のフィールド区切り文字を制御し、CSV、TSV、ログ行を整った列に分割します。 ブラウザで直接実行するハンズオンコードでDevOps Bootcampを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
DevOps Bootcampを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのDevOps Bootcampは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン1/4です。
「awkのレコード、フィールド、カスタム区切り文字」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このDevOps Bootcampレッスンでコードを書いて実行できますか?
はい。すべてのDevOps Bootcampレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- awkのレコード、フィールド、カスタム区切り文字
- パターン、範囲、BEGIN/ENDブロック
- awk配列とグループ化による集計
- awk関数、printfによる書式設定、レポート生成