【Shell Script】sed・awkと連携した文字列操作の基本:シェルスクリプトでのテキスト処理

Shell Script

こんにちは、かつコーチです。

シェル関数まで押さえたところで、ここからは実践Tips編に入ります。

シェルスクリプトの実務でログ解析や設定ファイルの一括編集をしていると、grepだけでは物足りず、sedawkの出番が必ず来ます。

前提知識ありきで、この2つの基本的な使い分けと、実際にハマりやすい落とし穴を解説します。

sedとawkとは:役割の整理

シェルスクリプトの中でテキストを加工する場面では、sedawkという2つのコマンドが中心的な役割を果たします。

sed(Stream Editor)は、テキストの「特定の文字列を置換・削除する」ことに特化したコマンドです。

awkは、テキストを「列(フィールド)単位で処理・集計する」ことに特化したプログラミング言語兼コマンドです。

校正の現場に例えると分かりやすいです。

sedは原稿の中の特定の言葉を機械的に一括置換する校正ロボット、awkは表形式のデータを列ごとに読み取って集計する表計算ソフトのミニ版というイメージです。

「文字列そのものを書き換えたい」ときはsed、「列に分かれたデータを取り出したり計算したりしたい」ときはawk、とまず役割で覚えておくと使い分けに迷いません。

sedの基本的な使い方

文字列置換:s/対象/置換後/

sedの最も基本的な使い方が、sコマンドによる置換です。

#!/bin/bash

echo "environment=production" | sed 's/production/staging/'
# environment=staging

ファイルを直接書き換えたい場合は-i(in-place)オプションを使います。

sed -i 's/production/staging/' config.env

置換対象がファイル内に複数ある場合は、末尾にg(グローバル)フラグを付けます。

sed -i 's/localhost/127.0.0.1/g' hosts.conf

行の削除・抽出

行単位の操作もsedの得意分野です。

#!/bin/bash

# 3行目を削除
sed '3d' access.log

# 空行だけを削除
sed '/^$/d' access.log

# "ERROR"を含む行だけを抽出(grepと同じ動きをsedでも書ける)
sed -n '/ERROR/p' access.log

-nは「通常の出力を抑制する」オプションで、p(print)コマンドと組み合わせることで「マッチした行だけを表示する」動きになります。

awkの基本的な使い方

列(フィールド)の抽出とFS/OFS

awkはテキストを自動的にスペースまたはタブで区切り、$1$2…というフィールド変数として扱います。

#!/bin/bash

echo "2026-09-08 10:23:01 ERROR disk full" | awk '{print $3}'
# ERROR

区切り文字がスペース以外(CSVのカンマなど)の場合は、-Fオプションでフィールドセパレータ(FS)を指定します。

echo "山田,営業部,課長" | awk -F',' '{print $2}'
# 営業部

出力時の区切り文字を変えたい場合はOFS(出力フィールドセパレータ)を指定します。

echo "山田,営業部,課長" | awk -F',' -v OFS='\t' '{print $1, $3}'
# 山田    課長

条件付き処理とパターンマッチ

awkは「パターン { アクション }」という構文で、条件に一致した行だけを処理できます。

#!/bin/bash

# access.logの中から、レスポンスタイムが1秒(1000ms)を超える行だけ抽出
awk '$5 > 1000 {print $1, $5}' access.log

# ERRORを含む行の件数を数える
awk '/ERROR/ {count++} END {print count "件のエラー"}' app.log

ENDブロックは、全行の処理が終わったあとに一度だけ実行される特別なブロックで、合計や件数の集計によく使います。

よくあるつまずきポイント・エラー対処

macOS流儀のsed -iをUbuntuで実行してエラーになる

sedの-iオプションの書き方は、GNU sed(Linux標準)とBSD sed(macOS標準)で微妙に異なります。

macOSの記事やStack Overflowの回答を参考にしていると、次のような書き方を見かけることがあります。

❌ Before:macOS流儀(BSD sed)の書き方をそのままUbuntuで実行する

sed -i '' 's/foo/bar/' config.txt

これをUbuntu 26.04(GNU sed)で実行すると、次のエラーになります。

sed: -e expression #1, char 1: unknown command: `''

実際に私も、macOSで書かれたシェルスクリプトのサンプルをそのままUbuntuサーバーにコピーして実行し、このエラーに遭遇したことがあります。

原因は、BSD sedでは-iの直後にバックアップ用の拡張子(空文字列でも可)を別引数として渡す必要があるのに対し、GNU sedでは-i単体で完結する、という仕様差です。

✅ After:GNU sed(Ubuntu標準)の書き方に合わせる

# 拡張子なしで直接上書き
sed -i 's/foo/bar/' config.txt

# バックアップを残したい場合はオプションと拡張子を続けて書く
sed -i.bak 's/foo/bar/' config.txt

Ubuntu環境で運用するスクリプトを書くときは、参考にする記事がmacOS向けかLinux向けかを必ず確認する習慣をつけておくと、このエラーを避けられます。

正規表現の特殊文字エスケープ忘れ

置換対象の文字列にパスのような/が含まれていると、sedの区切り文字と衝突してエラーになります。

❌ Before:区切り文字と同じ/を含む文字列をそのまま置換しようとする

sed 's//var/www/old//var/www/new/' deploy.conf

これを実行すると、次のようなエラーになります。

sed: -e expression #1, char 6: unknown option to `s'

✅ After:区切り文字を/以外(#|など)に変更する

sed 's#/var/www/old#/var/www/new#' deploy.conf

sedのsコマンドの区切り文字は/固定ではなく、任意の記号に変更できます。

パスを置換する機会は実務で非常に多いため、この#区切りのパターンは覚えておくと役立ちます。

応用・一歩先の使い方

sedとawkをパイプで組み合わせてログを集計する

実務では、sedで前処理をしてからawkで集計する、という組み合わせがよく使われます。

#!/bin/bash

# タイムスタンプの秒以下を削除してから、ステータスコードごとに件数を集計
sed -E 's/(:[0-9]{2})\.[0-9]+//' access.log \
    | awk '{print $9}' \
    | sort \
    | uniq -c \
    | sort -rn

このように、1つのコマンドで無理に完結させようとせず、「sedで整形」「awkで集計」「sort・uniqで数える」と役割ごとにパイプでつなげるのが、シェルスクリプトらしい書き方です。

複雑な正規表現を1つのsedコマンドに詰め込むより、小さな処理をパイプでつなぐほうが、後から読んだときの可読性も保てます。

まとめ

この記事のポイント

  • sedは文字列の置換・削除、awkは列単位の処理・集計に向いている
  • sed -iはGNU sed(Ubuntu標準)とBSD sed(macOS標準)で書き方が異なるため、参考記事のOSに注意する
  • 置換対象に/を含む場合は、区切り文字を#などに変更するとエラーを避けられる
  • 複雑な処理は1コマンドに詰め込まず、sed・awk・sort・uniqをパイプでつなげると読みやすい

次に読むべき記事

  • set -eなどエラーハンドリングの基本
  • cronで定期実行するスクリプトを組む

タグ: Shell, 上級者向け, 実践Tips

タイトルとURLをコピーしました