Help us understand the problem. What is going on with this article?

どのUNIXコマンドでも使える正規表現

More than 3 years have passed since last update.

特定コマンドの正規表現で使えるメタ文字が何だかわからない!

正規表現自体は知っているけど、それが今から使おうとしているコマンドで使えるものなのかわからずに困っている人も多いと思うのでまとめてみた。

たった3つの正規表現メタ文字セットだけ知ればいい

コマンドによって、対応している正規表現メタ文字の範囲には違いがある。しかし、最低限知っておけばよいのは2種類+1サブセットの3つだけだ。

  1. BRE(基本正規表現)メタ文字セット
  2. ERE(拡張正規表現)メタ文字セット
    1. AWKのサブセット

もちろん、これ以外にもGNU拡張正規表現メタ文字セットやPerl拡張正規表現メタ文字セット、JavaScript拡張正規表現メタ文字セットなどいくつかあるのだが、「どのUNIXでも(=POSIXで)使える」という特長を持たせたいのであれば、それらは覚えても意味がないので上記の3つさえおさえておけばよい。(例えPOSIXに拘らないとしても、他のものは大抵EREの拡張になっているので、一旦EREを覚えておくと整理しやすいと思う)

各コマンドは、どのメタ文字セットに対応しているか

3種類あることがわかったところで、各コマンドがそれら3つのうちどれに対応しているかをまとめたのが次の表だ。この表を見て、対応しているメタ文字セットがわかったら、次節のメタ文字セットの各一覧を見ればよい。

コマンド 対応しているメタ文字セット
AWK EREのAWKサブセット
ed BREメタ文字セット
egrep EREメタ文字セット
ex BREメタ文字セット
grep('-E'なし) BREメタ文字セット
grep('-E'あり) EREメタ文字セット
more BREメタ文字セット
sed BREメタ文字セット
vi BREメタ文字セット

参考までに述べておくと、GNU拡張やPerl拡張、JavaScript拡張は、いずれもEREのスーパーセットである。ということはすなわち、EREメタ文字セットを覚えておけばそれらの上でも動くということだ。

また、多くのgrep(-Eなし)では、一部のEREメタ文字がバックスラッシュ付きで使えたりする(例えば\+\|)が、それらはGNU拡張であってgrep本来のものではない。

各メタ文字セットの説明

目的のコマンドがどのメタ文字セットに対応しているかわかったところで、文字セットを各々紹介する。

1. BRE(基本正規表現)メタ文字セット

(BREに限らないが)メタ文字セットの中は、メタ文字を使う場所に応じてさらに3つのグループに分類される。それを踏まえて読んでもらいたい。

a-1. マッチを掛ける文字列(置換前の文字列)のメタ文字一覧(ブラケット外部)

まずはブラケット記号の外部についてまとめる。ブラケット内部ではこれらの多くのメタ文字が意味を失い、また意味が変わったり、ブラケット内部でのみ意味を持つメタ文字が新たに登場するため、次項でまとめることにする。

メタ文字 意味
^ 文字列(通常は行)の先頭にマッチ(先頭以外では通常文字と見なされる)
$ 文字列(通常は行)の末尾にマッチ(末尾以外では通常文字と見なされる)
. 任意の1文字にマッチ
[] []で囲まれた中で列挙した文字のいずれか1文字にマッチ
[^] [^]で囲まれた中で列挙した文字以外の任意の1文字にマッチ
* 【繰返し指定子】直前に記述した文字が0文字以上連続していることを指定し、後続の繰返し指定子よりも優先して可能な限り最大数マッチさせようとする。
\{n\} 【繰返し指定子】直前に記述した文字がn文字連続していることを指定する。
\{n,\} 【繰返し指定子】直前に記述した文字がn文字以上連続していることを指定し、後続の繰返し指定子よりも優先して可能な限り最大数マッチさせようとする。
\{m,n\} 【繰返し指定子】直前に記述した文字がm文字以上、n文字以下連続していることを指定し、後続の繰返し指定子よりも優先して可能な限り最大数マッチさせようとする。
\(\) 【包括指定子】\(\)で囲まれた範囲の文字列を、上記の繰返し指定子の1文字として扱わせたい場合、もしくはsed等で置換後に再利用したい文字列範囲を指定したい場合に用いる。
\n 【後方参照子】n番目に記した包括指定子でマッチした文字列にマッチする。例えば、ABC123ABCABCという文字列を^\([A-Z]*\)123\1*$という正規表現文字列に掛ければ\1ABCという文字列と見なされるため、この場合\1*は末尾にある2つのABCにマッチする。
\x 上記のうちでバックスラッシュで始まらないメタ文字自身、あるいはAWKやsedなどで正規表現の始まりを示すために用いた文字自身を指定したい場合に、xの部分にその文字を記述すればそれにマッチ
\\ バックスラッシュ\自身にマッチ

a-2. マッチを掛ける文字列(置換前の文字列)のメタ文字一覧(ブラケット内部)

既に述べたように、ブラケット([から])で囲まれた区間はその外側とは使えるメタ文字が違う。

メタ文字 意味
^ 開きブラケットの直後(-]よりも優先)に記述すると、否定の意味になる。もし^自身を指定したい場合は、2文字目以降に記述する(^の1文字しか指定するものがない場合はそもそも[~]で囲う必要がない)。
- 文字を列挙する代わりに範囲で指定できる。例えばA-Zならば文字AからZを全て列挙したことと等価。もし-自身を指定したい場合は、閉じブラケットの直前[よりも優先)に記述する。
[ 閉じブラケット]の直前(だだし-自身も指定する場合は-の方が優先)に記述すると[自身を指定することができる。
] 開きブラケット[の直後に記述すると]自身を指定することができる。
\x AWKやsedなどで正規表現の始まりを示すために用いた文字自身を指定したい場合に、xの部分にその文字を記述するとその文字自身を指定することができる。
\\ バックスラッシュ\自身を指定したい場合に用いる。

実はBRE(後述のEREも含む)では、上記に加えて次のメタ文字列が定義されているのだが、間違った実装がされていたり、使える実装にお目にかかったことがなかったり、といったものであるため、使うことはお勧めできない。

メタ文字 意味
[:word:] POSIX文字クラスと呼ばれる。wordの部分にはalnum(アルファベットと数字全部)、cntrl(制御文字全部)、lower(アルファベット小文字全部)、space(スペースとタブと改ページ)、alpha(アルファベット全部)、digit(数字全部)、print(制御文字以外の文字全部)、upper(アルファベット大文字全部)、blank(スペースとタブ)、graph(制御文字とスペース、タブ以外全部)、punct(句読点全部)、xdigit(16進数文字全部)が指定できる。実際に使うときは[[:lower:][:blank:]]などのように使う。が、一部の実装ではブラケット記号が一重でないと動かないといった間違った実装になっているものがある。
[.word.] 例えば[[.hoge.]]と記述したら、\(hoge\)\{1,\}と等価な意味を持つようだ。が、使える実装を見たことがない。後者の記述で事足りるからか?
[=x=] 例えば[=a=]と記述したら、aにもàにもâにもマッチするもので、実際に使う時は[[=a=]bc]のように記述する。が、これも使える実装を見たことがない。アクセント記号付の文字を素直に書き並べれば済むので無くても事足りるからか?

b. 置換後の文字列指定(sed等のs/A/B/におけるBの部分)で使えるメタ文字一覧

正規表現は、マッチする文字列を検索するためだけでなく、マッチしたその文字列を加工(置換)するためにも用いられる。sedコマンドにおけるs/A/B/はそのための代表的な書式であるが、このBの部分で使えるメタ文字を次の表に示す。

メタ文字 意味
\n n番目に指定した包括指定子(\(\))で囲まれた範囲にマッチした文字列に置き換えられる。
& マッチした文字列全体に置き換えられる。
\x 上記のメタ文字(&)、またsedなどで正規表現の始まりを示すために用いた文字自身を指定したい場合、xの部分に記せばその文字自身を指定することができる。
\\ バックスラッシュ\自身を指定したい場合に用いる。

2. ERE(拡張正規表現)メタ文字セット

a-1. マッチを掛ける文字列(置換前の文字列)のメタ文字一覧(ブラケット外部)

BREを拡張して使えるメタ文字が増えている(+?|)が、純粋な上位互換ではないので注意。具体的には、バックスラッシュでエスケープしていた括弧類のメタ文字(\(\)\{\})がバックスラッシュ不要になっている点、そして後方参照が保証されていない(実際に使えない実装がある)点である。

メタ文字 意味
^ 文字列(通常は行)の先頭にマッチ(文字列全体の先頭や後述の包括指定子内の先頭以外で\によるエスケープをせずに使うと通常文字として扱われない環境があるので注意)
$ 文字列(通常は行)の末尾にマッチ(文字列全体や末尾や後述の包括指定子内の末尾以外で\によるエスケープをせずに使うとエラー扱いされる環境があるので注意)
. 任意の1文字にマッチ
[] []で囲まれた中で列挙した文字のいずれか1文字にマッチ
[^] [^]で囲まれた中で列挙した文字以外のいずれか1文字にマッチ
* 【繰返し指定子】直前に記述した文字が0文字以上連続していることを指定し、後続の繰返し指定子よりも優先して可能な限り最大数マッチさせようとする。
+ 【繰返し指定子】直前に記述した文字が1文字以上連続していることを指定し、後続の繰返し指定子よりも優先して可能な限り最大数マッチさせようとする。
{n} 【繰返し指定子】直前に記述した文字がn文字連続していることを指定する。
{n,} 【繰返し指定子】直前に記述した文字がn文字以上連続していることを指定する。
{m,n} 【繰返し指定子】直前に記述した文字がm文字以上、n文字以下連続していることを指定し、後続の繰返し指定子よりも優先して可能な限り最大数マッチさせようとする。
? 【繰返し指定子】直前に記述した文字が0文字以上1文字以下連続していることを指定する。あるいは上記の各種繰返し指定子の直後に記述することで、可能な限り最小数マッチするように振る舞いを切り替えさせる。
() 【包括指定子】()で囲まれた範囲の文字列を、上記の繰返し指定子の1文字として扱わせたい場合、もしくはsed等で置換後に再利用したい文字列範囲を指定したい場合に用いる。または、後述の論理和指定子の範囲を限定したい場合に用いる。
| 【論理和指定子】この指定子の左の文字列または右の文字列でマッチさせることを指定する。左右の範囲は、前述の包括指定子の中であればその始端または終端まで、無ければ正規表現文字列全体の始端または終端まで(^$をも内包させる)と見なされる。例えば^ABC|DEF$は、^(ABC|DEF)$ではなく(^ABC)|(DEF$)の意味に解釈される。
\x 上記のうちでバックスラッシュで始まらないメタ文字自身、あるいはAWKやsedなどで正規表現の始まりを示すために用いた文字自身を指定したい場合に、xの部分にその文字を記述すればそれにマッチ
\\ バックスラッシュ\自身にマッチ

a-2. マッチを掛ける文字列(置換前の文字列)のメタ文字一覧(ブラケット内部)

これはBREと同じ。

b. 置換後の文字列指定(sedで言うならs/A/B/におけるBの部分)で使えるメタ文字一覧

これもBREと同じ。ただ、置換後文字列を指定できるコマンドでEREに対応しているものはPOSIXの範囲では存在しない。(AWKは後述するのでここでは除く)

2'. AWKで使えるメタ文字セット

AWKは基本的にEREのメタ文字セットに対応しているのだが、一部のメタ文字に対応していない。具体的にはブレースを用いた繰返し指定子({})だ。これは結構痛い。

一応2008年版のPOSIXではこれも含めてEREに完全対応するように勧告されたようなのだが、まだ年数が浅いために現存するAWK実装で対応しているものは少なく、実質的に完全なEREは通用しない。

またAWKでは、正規表現を制御する各構文や関数に正規表現文字列が渡される前に、AWK言語としてのエスケープ処理がなされるので注意が必要だ。具体的には、次のとおりである。

文字 置換される文字
\\ バックスラッシュ\に置換される。
\/ スラッシュ/に置換される。
\" ダブルクォーテーション"に置換される。
\ddd dddが3桁の8進数である時、その値の文字コードに該当する文字に置換される。(→補足
\a ビープ音(BEL:文字コード0x07)に置換される。
\b バックスペース(BS:文字コード0x08)に置換される。
\f 改ページ(FF:文字コード0x0c)に置換される。
\n 改行(LF:文字コード0x0a)に置換される。
\r 行頭復帰(CR:文字コード0x0d)に置換される。
\t 水平タブ(HT:文字コード0x09)に置換される。
\v 垂直タブ(VT:文字コード0x0b)に置換される。
\(上記以外) 未定義(通常は単にバックスラッシュを除いた文字に置換される)

以上をまとめると、AWKで使えるメタ文字セットをまとめると次のとおりである。

a-1. マッチを掛ける文字列(置換前の文字列)のメタ文字一覧(ブラケット外部)

ブレースを用いた繰返し指定子({})以外のEREメタ文字セット

ただし、バックスラッシュで始まる文字列を与えると先程の表のとおりのエスケープ処理を受ける。

a-2. マッチを掛ける文字列(置換前の文字列)のメタ文字一覧(ブラケット内部)

BREと同じ。ただし、バックスラッシュで始まる文字列を与えると先程の表のとおりのエスケープ処理を受ける。

b. 置換後の文字列指定(sub,gsub関数の第2引数)で使えるメタ文字一覧

これも基本的にはBREと同じなのだが、次の2点に注意しなければならない。

  • \n(後方参照子)には対応していない(これに対応しているのはGNU AWKで追加されたgensubという関数である)。
  • 先程の表のとおりのエスケープ処理を受ける。このため、特に注意が必要なのは、メタ文字として予約されている&自身を指定したい場合である。具体的には、ダブルクォーテーションの内側で\&と書きたいのであれば、バックスラッシュ\がエスケープされないように\\&と記さなければならない。

c. AWKにおける補足

GNU版AWKの古いバージョン(3.1.5で確認)では、ブラケット([])と8ビットアスキーコード(\200\377)を組み合わせた場合にバグがあることが確認できた。そのため、どこの環境のAWKでも動くような正規表現にしたければ次の点に気を付けなければならない。

マルチバイト文字列に反応する正規表現をAWK(GNU版3.1.5)で書こうとすると……
$ export LANGUAGE=POSIX
$ export LANG=C
$ export LC_ALL=C
$ echo 123あいう | awk '/[\200-\377]/{print "multi-byte"}'
awk: fatal: Invalid collation character: /[?-?]/
$ 

これはどういうことかというと、ロケール環境変数でいくら非マルチバイト文字列用のモードにしてもブラケット([])の中ではマルチバイト扱いを無効化することができず、「不完全なマルチバイト文字である」としてエラーにされてしまうのだ。

ちなみに、grepではこの現象は確認されていない。

GNU版grepは大丈夫
$ export LANGUAGE=POSIX
$ export LANG=C
$ export LC_ALL=C
$ a=$(printf '\200')
$ b=$(printf '\377')
$ echo 123あいう | grep '['$a'-'$b']'
123あいう
$ 

仕方が無いので、もし同様のことをやりたいのであれば次のように書くのが無難だ。

古いGNU版AWKに配慮したマルチバイト文字検出のしかた
$ echo 123あいう | awk '$0 !~ /^[\001-\177]*$/{print "multi-byte"}'
multi-byte
$ 

条件を反転して検出している。ただし、GNU AWK以外の実装ではNULLが扱えないので\000を指定してはいけない。そもそも文字列にNULLなど含まれるわけがないという前提で\001以上の範囲で検査させなければならない。

参考文献

POSIX 1003.1の9章“Regular Expressions

richmikan@github
困った時はすぐQiitaを始めとしたTipsを表面的に鵜呑みにし、使えそうなプロダクトを拾ってきてマニュアル通りに対応するプロダクト至上主義者達よ。そんなことでは、想定外の事態に見舞われた時すぐ死ぬぞ。想定外とは、マニュアルには載ってないから想定外なのだ。マニュアル通りにしか動けぬ者は、典型的なコンビニ店員の如く薄給に喘ぐだけ。頭を使え!UNIX哲学に目覚めよ!そしてPOSIX原理主義を崇拝せよ!
https://github.com/ShellShoccar-jpn
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした