正規表現
| 対応: | grep -E / sed(外部コマンド) | POSIX(sh互換) |
|---|---|---|
| [[ =~ ]] / BASH_REMATCH | Bash(bash拡張) |
『シェルスクリプト』では正規表現を使った文字列マッチングを複数の方法で行えます。bash の [[ $var =~ regex ]] はスクリプト内で条件分岐と組み合わせるのに適しており、マッチ結果を BASH_REMATCH 配列でキャプチャできます。外部コマンドの grep -E は拡張正規表現でファイルや標準入力を絞り込むのに使い、sed は正規表現を使った置換・削除・抽出に使います。それぞれの特性を理解して使い分けることが重要です。
構文
# -----------------------------------------------
# [[ =~ ]] — bash の正規表現マッチ
# -----------------------------------------------
# 基本的な書き方です
if [[ "$変数" =~ 正規表現パターン ]]; then
echo "マッチしました"
fi
# キャプチャグループを使ってマッチ部分を取り出します
if [[ "$変数" =~ ^(グループ1)(グループ2)$ ]]; then
echo "${BASH_REMATCH[0]}" # マッチした全体
echo "${BASH_REMATCH[1]}" # 1番目のキャプチャグループ
echo "${BASH_REMATCH[2]}" # 2番目のキャプチャグループ
fi
# -----------------------------------------------
# grep -E — 拡張正規表現でファイル・入力を絞り込みます
# -----------------------------------------------
# ファイル内でパターンにマッチした行を表示します
grep -E "パターン" ファイル名
# マッチした行番号も表示します(-n オプション)
grep -En "パターン" ファイル名
# マッチした行だけをカウントします(-c オプション)
grep -Ec "パターン" ファイル名
# マッチしない行を表示します(-v オプション)
grep -Ev "パターン" ファイル名
# -----------------------------------------------
# sed — 正規表現を使った置換・削除・抽出
# -----------------------------------------------
# 最初のマッチだけを置換します
sed "s/パターン/置換後/g" ファイル名
# グループを参照して置換します(\1 で1番目のグループを参照)
sed "s/\(グループ1\)\(グループ2\)/\2\1/" ファイル名
# -E オプションで拡張正規表現を使います(\ なしでグループを書けます)
sed -E "s/(グループ1)(グループ2)/\2\1/" ファイル名
# マッチした行を削除します(d コマンド)
sed -E "/パターン/d" ファイル名
構文一覧
| 構文 / コマンド | 概要 |
|---|---|
[[ $var =~ regex ]] | bash の複合コマンドで POSIX 拡張正規表現マッチを行います。マッチ結果は BASH_REMATCH に格納されます。 |
BASH_REMATCH[0] | 直前の =~ マッチで一致した文字列全体が格納されます。 |
BASH_REMATCH[N] | 直前の =~ マッチで N 番目のキャプチャグループ () にマッチした文字列が格納されます。 |
grep -E "pattern" | 拡張正規表現(ERE)で行をフィルタリングします。egrep と同等です。 |
grep -En "pattern" | マッチした行を行番号付きで表示します。 |
grep -Eo "pattern" | マッチした部分だけを1行1マッチで抽出します。 |
grep -Ev "pattern" | パターンにマッチしない行だけを表示します。 |
grep -Ec "pattern" | マッチした行数をカウントして表示します。 |
sed "s/pattern/replace/" | 各行の最初のマッチを置換します。 |
sed "s/pattern/replace/g" | 各行のすべてのマッチを置換します。 |
sed -E "s/(g1)(g2)/\2\1/" | -E で拡張正規表現を使い、\N でキャプチャグループを参照して置換します。 |
sed -E "/pattern/d" | パターンにマッチした行を削除します。 |
^ / $ | 行頭 / 行末にマッチするアンカーです。 |
. | 改行を除く任意の1文字にマッチします。 |
* / + / ? | 直前の要素の 0回以上 / 1回以上 / 0か1回にマッチします(+? は拡張正規表現)。 |
[abc] / [^abc] | 文字クラスです。[] 内のいずれか1文字 / [] 内以外の1文字にマッチします。 |
() | グループ化とキャプチャを行います(拡張正規表現)。 |
| | 交替(OR)です。左右どちらかにマッチします(拡張正規表現)。 |
サンプルコード
dragonball_rematch.sh
#!/bin/bash
# -----------------------------------------------
# ドラゴンボールのキャラクターデータで
# [[ =~ ]] と BASH_REMATCH の使い方を確認します
# -----------------------------------------------
# -----------------------------------------------
# 「名前:戦闘力:種族」形式のデータリストです
# -----------------------------------------------
records=(
"孫悟空:3000000:サイヤ人"
"ベジータ:2500000:サイヤ人"
"ピッコロ:1100000:ナメック星人"
"フリーザ:530000:フリーザ族"
"クリリン:75000:人間"
)
echo "=== 基本マッチ: サイヤ人を抽出します ==="
echo ""
for rec in "${records[@]}"; do
# =~ で「サイヤ人」を含むレコードにマッチします
if [[ "$rec" =~ サイヤ人 ]]; then
echo " [サイヤ人] $rec"
fi
done
echo ""
echo "=== キャプチャ: 名前と戦闘力を分離して取り出します ==="
echo ""
for rec in "${records[@]}"; do
# コロン区切りで3フィールドをキャプチャします
# グループ1: 名前(コロン以外の文字列)
# グループ2: 戦闘力(数字のみ)
# グループ3: 種族(残りすべて)
if [[ "$rec" =~ ^([^:]+):([0-9]+):(.+)$ ]]; then
name="${BASH_REMATCH[1]}"
power="${BASH_REMATCH[2]}"
race="${BASH_REMATCH[3]}"
printf " %-12s 戦闘力: %7s 種族: %s\n" "$name" "$power" "$race"
fi
done
echo ""
echo "=== 戦闘力が100万以上のキャラクターを抽出します ==="
echo ""
for rec in "${records[@]}"; do
# 7桁以上の数字を持つレコードを抽出します
if [[ "$rec" =~ :([0-9]{7,}): ]]; then
echo " [100万超] $rec → キャプチャ: ${BASH_REMATCH[1]}"
fi
done
$ chmod +x dragonball_rematch.sh $ ./dragonball_rematch.sh === 基本マッチ: サイヤ人を抽出します === [サイヤ人] 孫悟空:3000000:サイヤ人 [サイヤ人] ベジータ:2500000:サイヤ人 === キャプチャ: 名前と戦闘力を分離して取り出します === 孫悟空 戦闘力: 3000000 種族: サイヤ人 ベジータ 戦闘力: 2500000 種族: サイヤ人 ピッコロ 戦闘力: 1100000 種族: ナメック星人 フリーザ 戦闘力: 530000 種族: フリーザ族 クリリン 戦闘力: 75000 種族: 人間 === 戦闘力が100万以上のキャラクターを抽出します === [100万超] 孫悟空:3000000:サイヤ人 → キャプチャ: 3000000 [100万超] ベジータ:2500000:サイヤ人 → キャプチャ: 2500000 [100万超] ピッコロ:1100000:ナメック星人 → キャプチャ: 1100000
dragonball_grep.sh
#!/bin/bash
# -----------------------------------------------
# ドラゴンボールのキャラクターデータファイルに
# grep -E で様々なフィルタリングを行います
# -----------------------------------------------
# -----------------------------------------------
# 作業用データファイルを作成します
# -----------------------------------------------
DATA_FILE="/tmp/dragonball_chars.txt"
cat > "$DATA_FILE" <<'EOF'
孫悟空:3000000:サイヤ人:主人公
ベジータ:2500000:サイヤ人:ライバル
ピッコロ:1100000:ナメック星人:師匠
フリーザ:530000:フリーザ族:悪役
クリリン:75000:人間:親友
トランクス:1800000:サイヤ人:未来から来た戦士
EOF
echo "=== ファイルの内容 ==="
cat "$DATA_FILE"
echo ""
echo "=== grep -E: サイヤ人のみ表示します ==="
grep -E "サイヤ人" "$DATA_FILE"
echo ""
echo "=== grep -En: 行番号付きで戦闘力100万以上を表示します ==="
# 戦闘力フィールドが7桁以上の行を抽出します
grep -En ":[0-9]{7,}:" "$DATA_FILE"
echo ""
echo "=== grep -Ev: サイヤ人以外を表示します ==="
grep -Ev "サイヤ人" "$DATA_FILE"
echo ""
echo "=== grep -Ec: サイヤ人の人数をカウントします ==="
count=$(grep -Ec "サイヤ人" "$DATA_FILE")
echo "サイヤ人の数: ${count} 人"
echo ""
echo "=== grep -Eo: 名前フィールドだけを抽出します ==="
# 行頭からコロンまでの部分(名前)だけを取り出します
grep -Eo "^[^:]+" "$DATA_FILE"
echo ""
echo "=== grep -E で OR 条件: 悪役または親友を表示します ==="
grep -E "悪役|親友" "$DATA_FILE"
# 作業用ファイルを削除します
rm -f "$DATA_FILE"
$ chmod +x dragonball_grep.sh $ ./dragonball_grep.sh === ファイルの内容 === 孫悟空:3000000:サイヤ人:主人公 ベジータ:2500000:サイヤ人:ライバル ピッコロ:1100000:ナメック星人:師匠 フリーザ:530000:フリーザ族:悪役 クリリン:75000:人間:親友 トランクス:1800000:サイヤ人:未来から来た戦士 === grep -E: サイヤ人のみ表示します === 孫悟空:3000000:サイヤ人:主人公 ベジータ:2500000:サイヤ人:ライバル トランクス:1800000:サイヤ人:未来から来た戦士 === grep -En: 行番号付きで戦闘力100万以上を表示します === 1:孫悟空:3000000:サイヤ人:主人公 2:ベジータ:2500000:サイヤ人:ライバル 3:ピッコロ:1100000:ナメック星人:師匠 6:トランクス:1800000:サイヤ人:未来から来た戦士 === grep -Ev: サイヤ人以外を表示します === ピッコロ:1100000:ナメック星人:師匠 フリーザ:530000:フリーザ族:悪役 クリリン:75000:人間:親友 === grep -Ec: サイヤ人の人数をカウントします === サイヤ人の数: 3 人 === grep -Eo: 名前フィールドだけを抽出します === 孫悟空 ベジータ ピッコロ フリーザ クリリン トランクス === grep -E で OR 条件: 悪役または親友を表示します === フリーザ:530000:フリーザ族:悪役 クリリン:75000:人間:親友
dragonball_sed.sh
#!/bin/bash # ----------------------------------------------- # ドラゴンボールのキャラクターデータで # sed の正規表現置換・削除・抽出を確認します # ----------------------------------------------- # ----------------------------------------------- # 作業用データを変数に用意します # ----------------------------------------------- DATA="孫悟空:3000000:サイヤ人 ベジータ:2500000:サイヤ人 ピッコロ:1100000:ナメック星人 フリーザ:530000:フリーザ族 クリリン:75000:人間" echo "=== 元データ ===" echo "$DATA" echo "" echo "=== sed -E s: 戦闘力フィールドを「機密」に置換します ===" # 数字列(戦闘力フィールド)を「機密」に置換します echo "$DATA" | sed -E "s/:[0-9]+:/:機密:/" echo "" echo "=== sed -E キャプチャ参照: 名前と種族だけを出力します ===" # グループ1=名前, グループ2=戦闘力, グループ3=種族 として # \1 と \3 だけを使って再構成します echo "$DATA" | sed -E "s/^([^:]+):[^:]+:(.+)$/\1: \2/" echo "" echo "=== sed -E /d: サイヤ人の行を削除します ===" echo "$DATA" | sed -E "/サイヤ人/d" echo "" echo "=== sed -E 複数コマンド: 置換を連鎖させます ===" # 「サイヤ人」を「戦士」に、「人間」を「地球人」に置換します echo "$DATA" | sed -E "s/サイヤ人/戦士/g; s/人間/地球人/g" echo "" echo "=== sed で行頭のコロン区切り名前フィールドだけを抽出します ===" # 行頭の名前以降をすべて削除します(名前のみを残します) echo "$DATA" | sed -E "s/:.*$//"
$ chmod +x dragonball_sed.sh $ ./dragonball_sed.sh === 元データ === 孫悟空:3000000:サイヤ人 ベジータ:2500000:サイヤ人 ピッコロ:1100000:ナメック星人 フリーザ:530000:フリーザ族 クリリン:75000:人間 === sed -E s: 戦闘力フィールドを「機密」に置換します === 孫悟空:機密:サイヤ人 ベジータ:機密:サイヤ人 ピッコロ:機密:ナメック星人 フリーザ:機密:フリーザ族 クリリン:機密:人間 === sed -E キャプチャ参照: 名前と種族だけを出力します === 孫悟空: サイヤ人 ベジータ: サイヤ人 ピッコロ: ナメック星人 フリーザ: フリーザ族 クリリン: 人間 === sed -E /d: サイヤ人の行を削除します === ピッコロ:1100000:ナメック星人 フリーザ:530000:フリーザ族 クリリン:75000:人間 === sed -E 複数コマンド: 置換を連鎖させます === 孫悟空:3000000:戦士 ベジータ:2500000:戦士 ピッコロ:1100000:ナメック星人 フリーザ:530000:フリーザ族 クリリン:75000:地球人 === sed で行頭のコロン区切り名前フィールドだけを抽出します === 孫悟空 ベジータ ピッコロ フリーザ クリリン
概要
『シェルスクリプト』で正規表現を使う主な方法は3つあります。bash の [[ $var =~ regex ]] はスクリプト内の条件分岐に使い、マッチした文字列は BASH_REMATCH 配列でキャプチャできます。BASH_REMATCH[0] がマッチ全体、BASH_REMATCH[1] 以降がキャプチャグループです。grep -E は拡張正規表現でファイルや標準入力から行を絞り込む際に使い、-n(行番号)、-o(マッチ部分のみ抽出)、-v(否定フィルタ)、-c(件数カウント)などのオプションと組み合わせると強力です。sed -E は拡張正規表現を使った置換・削除・再構成に使い、\1/\2 でキャプチャグループを参照した置換が書けます。正規表現を使った条件分岐の詳細については test と [ ] と [[ ]] も合わせて参照してください。
記事の間違いや著作権の侵害等ございましたらお手数ですがこちらまでご連絡頂ければ幸いです。