正規表現とワイルドカード、そして記号の意味

  • 5
    いいね
  • 3
    コメント

要旨

・正規表現とワイルドカードは別物
・正規表現とワイルドカードでは同じ「*」の記号でも意味が異なる
 「正規表現」→直前の表現が0個以上
 「ワイルドカード」→長さ0文字以上の任意の文字列
・正規表現でワイルドカードの「*」と同意味を記述したい場合「.*」と記述する
・「ls -a *」と打った場合どうなるか?(2017/2/4追記)

ある日のこと

/etc/passwdの中に三人のユーザーがいました。
masayukikun:x:1007:1007::/home/masayukikun:/bin/sh
nobuyukikun:x:1008:1008::/home/nobuyukikun:/bin/sh
yukimurakun:x:1010:1010::/home/yukimurakun:/bin/sh

三人とも名前の最後は「kun」で終わっていますね。
そこで三人とも一気に表示するためにワイルドカードを使って、以下のように打ってみました
cat /etc/passwd |grep *kun
ところが何も出力されません!さあ、どういうことでしょう?

こういうことでした

ワイルドカードを使って

↑ここが間違い。
私は正規表現ワイルドカードを混同していたのだ。
まずはWikipediaの「ワイルドカード (情報処理)」の項目から引用しよう。

Unixのシェルでは、「?」(疑問符)を任意の一文字、「*」(アスタリスク)を長さ0文字以上の任意の文字列にマッチするパターンとして、コマンドライン上で、存在するファイルの名前に対して展開される(たとえば ac abc abd というファイルがある場合、cat a*c とコマンドを入力すると、cat abc ac のように展開されたうえで、cat プログラムが起動される)。

具体例を伴って、期待していた動作について記述されている。
記述通りの動作が再現されるであろう。
それでは、なぜgrepに失敗したのであろうか?
ここで今度はWikipediaの「正規表現」の項目から引用しよう。

*
アスタリスクは直前の表現が0個以上あることを示す。例えば、「go*gle」 は 「ggle」、「gogle」、「google」 などにマッチする。

こちらは微妙に期待していた動作と違う。
「go*gle」と打ったのであれば「goagle」や「gooagle」もヒットして欲しいものであるが、そうは問屋が卸さないようだ。
どうも、「cat a*c」と打ったときではワイルドカードとして解釈されるが、「grep *kun」と打ったときは正規表現として解釈されてしまうようだ。
(何故解釈に相違が生まれるのかはよく分からない。bashの機能としてはワイルドカードが実装されているが、個々のコマンドに出力結果を投げると正規表現として解釈されてしまうのだろうか?)

まとめると、私は正規表現とワイルドカードを混同しており「*」という記号さえ打てば「長さ0文字以上の任意の文字列」という意味になるのだといういささか乱暴な思い込みをしていたのだ。

どうも困っている人は私だけではなくて、海外にもいるようだ。
Ubuntuのフォーラムに似たような悩みが相談されており、いいヒントが見つかった。

Knowing that . means "any single character",

「.」は任意の文字列を意味するようだ。
すなわち、「.*」と記述すれば「任意の文字列が0個以上ある」という意味になる。
そこで以下のように打ってみた。

cat /etc/passwd |grep .*kun

すると、今度は問題なく三人組が表示された。
masayukikun:x:1007:1007::/home/masayukikun:/bin/sh
nobuyukikun:x:1008:1008::/home/nobuyukikun:/bin/sh
yukimurakun:x:1010:1010::/home/yukimurakun:/bin/sh

めでたしめでたしと思いながら、表示することに成功した時点にしてようやく、普段サクラエディタで検索をかけるときに「.*」という表現を使っていることに気付いた。どうも意味もよく理解せずに使っていたっぽい。
本件は自身のテキトーな理解が浮き彫りになったエピソードである。
後々振り返ってこのときのテキトーさを反省するためにも、自戒の念を込めてこの記事を初投稿するとしよう。

「.から始まる」ファイルを表示したい「ls -a」とかたくなに表示したくない「*」

(2017/02/06 追記)
まずは以下のように打ってみよう

# ls -a
.  ..  .masayukikun  .nobuyukikun  yukimurakun
# ls 
yukimurakun
# ls *
yukimurakun

さっきの三人組をファイル名にしてカレントディレクトリに作成した。うち二人はファイル名の先頭に「.」がついており、ついていないのはyukimurakunだけである。
「ls」コマンドは先頭に「.」がついているファイルについてはデフォルトで表示せず、「-a」オプションを付与することで表示するようになる。したがって、上記の出力結果に差異が発生している。また、ワイルドカードを使用しても、先頭に「.」がついたファイルは表示されない。
ここまでは皆様ご存知の通りだと思う。 では、以下のように打ったらどうなるであろうか?

# ls -a *

「.」付きのファイルを表示するための「-a」オプションと「*」を組み合わせた。どのように表示されるであろうか?読者の皆様も予測していただきたい。結果は!!!

# ls -a *
yukimurakun

「.」付きのファイルについては表示されなかった。
私は「.」付きの文字列を含めて全てのファイルが表示されると予想していたが、異なる結果であった。
探ってみたらITproの記事にワイルドカードの「*」の意味が記述されたものがあった。著作権的に引用してよいか微妙なので、ここには書かないが参考文献にリンクは張っておく。その記事の記述が正しければ、まあ納得はいく。(ITproの記事が何を典拠にしてそのように書いているかは不明である。孫引きだけではなく、自力で専門書から引用できればよいのだがなぁ。。。)

参考文献
Wikipedia「正規表現」
Wikipedia「ワイルドカード (情報処理)」
askubuntu「Grep: The asterisk(*) doesn't always work」
ITpro「Linuxコマンド逆引き大全【 ワイルド・カードとは 】