はじめに
正規表現は、特定の文字列そのものではなく、テキストの構造によって文字列を表します。同じパターン言語が多くの Linux ツールで使われていますが、ツールごとに用途は異なります。grep は一致する行を選択または抽出し、sed はストリームを変換し、awk はパターンとフィールドを意識した処理を組み合わせます。
この実験では、アンカーと文字クラスから始め、拡張された繰り返しと抽出へ進みます。その後、変換結果を保存する前に確認し、最後にフィールドの選択と集計を行います。パターンは引用符で囲み、特殊文字をシェル自身が解釈せず、テキスト処理ツールへ渡すようにします。
基本的な正規表現の構造で行を照合する
このステップでは、リテラルテキスト、アンカー、文字クラスを使って、行全体の位置を表します。
練習用ファイルを確認します。
cd /home/labex/project/regex-lab
nl -ba contacts.txt
リテラルテキストは、行内のどこにあっても一致します。
grep 'active' contacts.txt
^ はパターンを行の先頭に固定し、$ は行末に固定します。Alice のレコードと、2 桁の数字で終わるレコードを検索します。
grep '^Alice|' contacts.txt
grep '[0-9][0-9]$' contacts.txt
角括弧内では、文字クラスによって 1 文字を照合します。[ABC] は A、B、C のいずれかを意味します。
grep '^[ABC]' contacts.txt | tee abc-contacts.txt
このコマンドにより、Alice、Bob、Carol のレコードが出力されます。アンカー付きの数値に一致する行も保存します。
grep '[0-9][0-9]$' contacts.txt > two-digit-values.txt
拡張正規表現を使って一致箇所を抽出する
このステップでは、拡張正規表現を使って選択肢と繰り返しを表し、一致した部分文字列だけを抽出します。
grep -E を使うと、+(1 回以上)、?(0 回または 1 回)、|(選択)などの演算子を使用できます。active または pending のステータスフィールド全体に一致させます。
cd /home/labex/project/regex-lab
grep -E '\|(active|pending)\|' contacts.txt
メールアドレス形式のパターンは、次の要素から組み立てられます。
[[:alnum:]._-]+は、ローカル部で使用できる文字を 1 文字以上照合します。@はリテラル文字です。[[:alnum:].-]+は、ドメイン本体を 1 文字以上照合します。\.[[:alpha:]]{2,}は、ドットと 2 文字以上の英字によるサフィックスを照合します。
-o を使うと、一致した部分文字列だけを出力できます。
grep -Eo '[[:alnum:]._-]+@[[:alnum:].-]+\.[[:alpha:]]{2,}' contacts.txt | tee emails.txt
一致した行に番号を付け、抽出したアドレスの数を数えます。
grep -En '\|(active|pending)\|' contacts.txt
wc -l < emails.txt | tr -d ' ' > email-count.txt
cat email-count.txt
dave_at_example.com は @ を含まないため、このパターンには一致しません。
Sed で変換結果を確認する
このステップでは、入力を変更せずに sed でストリームを変換します。
置換の形式は s/pattern/replacement/flags です。各行の最初の a だけを置換し、その後、g フラグを使って小文字の a をすべて置換します。
cd /home/labex/project/regex-lab
sed 's/a/A/' contacts.txt
sed 's/a/A/g' contacts.txt
テキスト自体にスラッシュが含まれる場合や、別の区切り文字の方が分かりやすい場合は、異なる区切り文字を使えます。メールドメインを redacted.invalid に置換します。
sed -E 's#@[[:alnum:].-]+\.[[:alpha:]]{2,}#@redacted.invalid#g' contacts.txt
Sed のアドレスを使うと、コマンドを適用する行を選択できます。正確な |inactive| フィールドを含む行だけで、ステータスを置換します。
sed '/|inactive|/s/|inactive|/|disabled|/' contacts.txt | tee contacts-preview.txt
元のファイルにまだ inactive が含まれていることを確認します。
grep '|inactive|' contacts.txt
Sed でクリーンアップ済みファイルを作成する
このステップでは、複数の sed コマンドを組み合わせ、変換したストリームを新しいファイルに保存します。
services.csv のヘッダーは変更しません。データ行では、warn を warning に正規化し、最後の数値フィールドに ms サフィックスを追加します。-e で複数のコマンドを指定し、アドレス 2,$ で 2 行目から末尾までに適用範囲を限定します。
cd /home/labex/project/regex-lab
sed -E -e '2,$ s/,warn,/,warning,/' -e '2,$ s/([0-9]+)$/\1ms/' services.csv | tee services-clean.csv
ここでは、括弧で最後の数字をキャプチャし、置換文字列内で \1 を使ってそのキャプチャを再利用しています。
元のファイルと新しいファイルを比較します。
diff -u services.csv services-clean.csv || true
出力は意図的に変更されているため、diff が 0 以外のステータスを返すのは想定どおりです。|| true により、コピーしたコマンド列を続けて実行できます。
Awk でフィールドを選択して集計する
このステップでは、フィールド構造と計算の両方が必要な処理に awk を使います。
-F, は入力区切り文字をカンマに設定します。データ行だけを対象に、サービスとステータスを出力します。NR は現在のレコード番号です。
cd /home/labex/project/regex-lab
awk -F, 'NR > 1 {print $1, $2}' services.csv
3 番目の数値フィールドが 200 を超える行を選択します。
awk -F, 'NR > 1 && $3 > 200 {print $1, $3}' services.csv | tee slow-services.txt
合計と件数を累積し、END ブロックで平均を計算します。printf で数値の形式を指定します。
awk -F, 'NR > 1 {sum += $3; count++} END {printf "average_latency=%.1f\n", sum/count}' services.csv | tee latency-summary.txt
Awk のパターンによってアクションを実行するレコードを決めます。$1 や $3 などのフィールドを使うことで、構造化された計算を読みやすく記述できます。
まとめ
アンカー、文字クラス、選択肢、繰り返しから正規表現パターンを組み立てました。また、grep で行の選択と抽出を行い、sed で変換結果を確認して保存し、awk でフィールドを意識した選択と集計を行いました。次のチャレンジでは、完全なコマンド列を示されていない状態で、これらの考え方を適用します。



