Bashで正規表現をやろうとしたら 否定先読み(Negative Lookahead) が出来なくてハマった話
こんにちは。DI部の大高です。
最近、Bashで正規表現処理をする機会があり、がっつりハマったのでご紹介します。
やりたかったこと
やりたかったのは、^(?![0-9])[0-9A-Za-z_]+$
という正規表現パターンでのエラーチェックです。実際の挙動は以下のオンラインサービスで確認していました。
このパターンは「先頭は数字で始まっちゃ駄目だけど、英数字とアンダースコアを許容する」という正規表現パターンです。 01_NG
はNGで、OK_01
はOKみたいな感じです。
Bashで正規表現のチェックってどうするの?
BashではVersion 3から ~=
というオペレーターで比較できるよ!という情報を得たので、確認してみました。こんな感じです。(サンプルなので冗長に書いてます)
#!/bin/bash regex='^(?![0-9])[0-9A-Za-z_]+$' ok_text='OK_01' ng_text='01_NG' if [[ ($ok_text =~ $regex) ]]; then echo "OKパターンだよ" else echo "NGパターンだよ" fi if [[ ($ng_text =~ $regex) ]]; then echo "OKパターンだよ" else echo "NGパターンだよ" fi
で、実行すると両方とも NGパターンだよ
と言われてしまいました。そんな馬鹿な…!
何がいけないのか
色々調べたところ、「BashではPCRE(Perl互換の正規表現)である否定先読み(Negative Lookahead)をサポートしていない」ということが分かりました。
Bash double bracket regex comparison using negative lookahead error return 2 - Stack Overflow
The conditional expression [Bash Hackers Wiki]
Perl Compatible Regular Expressions - Wikipedia
どうすればいいの?
色々とワークアラウンドはありそうでしたが、一番シンプルな2つに分けるという方法を取ってみました。
bash - Regex negative matching trouble - Server Fault
先程のコードを書き換えると以下のようになります。否定条件と肯定条件を分ける感じです。
#!/bin/bash #regex='^(?![0-9])[0-9A-Za-z_]+$' positive_regex='^[0-9A-Za-z_]+$' negative_regex='^[0-9].*$' ok_text='OK_01' ng_text='01_NG' if [[ ($ok_text =~ $positive_regex) && !($ok_text =~ $negative_regex) ]]; then echo "OKパターンだよ" else echo "NGパターンだよ" fi if [[ ($ng_text =~ $positive_regex) && !($ng_text =~ $negative_regex) ]]; then echo "OKパターンだよ" else echo "NGパターンだよ" fi
この場合は、ちゃんと以下のように出力されました。
OKパターンだよ NGパターンだよ
まとめ
Bashでは否定先読み(Negative Lookahead)できないので、お気を付けください。
どなたかのお役に立てれば幸いです。それでは!