この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
こんにちは。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)できないので、お気を付けください。
どなたかのお役に立てれば幸いです。それでは!