文字列がBashスクリプトの正規表現と一致するかどうかを確認する


204

スクリプトが受け取る引数の1つは、次の形式の日付ですyyyymmdd

入力として有効な日付を取得したかどうかを確認したいと思います。

これどうやってするの?私は次のような正規表現を使用しようとしています:[0-9]\{\8}


形式が正しいかどうかの確認は簡単です。しかし、(組み込みの)bashで、日付が有効かどうかを確認できるとは思いません。
RedX 2014年

回答:


317

テストコンストラクトを[[ ]]正規表現の一致演算子とともに使用して=~、文字列が正規表現パターンに一致するかどうかを確認できます。

あなたの特定のケースでは、あなたは書くことができます:

[[ $date =~ ^[0-9]{8}$ ]] && echo "yes"

またはより正確なテスト:

[[ $date =~ ^[0-9]{4}(0[1-9]|1[0-2])(0[1-9]|[1-2][0-9]|3[0-1])$ ]] && echo "yes"
#           |^^^^^^^^ ^^^^^^ ^^^^^^  ^^^^^^ ^^^^^^^^^^ ^^^^^^ |
#           |   |     ^^^^^^^^^^^^^  ^^^^^^^^^^^^^^^^^^^^^^^^ |
#           |   |          |                   |              |
#           |   |           \                  |              |
#           | --year--   --month--           --day--          |
#           |          either 01...09      either 01..09     end of line
# start of line            or 10,11,12         or 10..29
#                                              or 30, 31

つまり、希望する形式に一致する正規表現をBashで定義できます。この方法でできること:

[[ $date =~ ^regex$ ]] && echo "matched" || echo "did not match"

ここで&&、テストが成功した||場合は後のコマンドが実行され、テストが失敗した場合は後のコマンドが実行されます。

これは、bashでのユーザー入力日付形式の検証における Aleks-Daniel Jakimenkoによるソリューションに基づいていることに注意してください。


他のシェルではgrepを使用できます。シェルがPOSIXに準拠している場合は、

(echo "$date" | grep -Eq  ^regex$) && echo "matched" || echo "did not match"

POSIXに準拠していない、あなたが行うことができます

echo "$date" | grep -Eq "^regex\$"; and echo "matched"; or echo "did not match"

19
私はそのことを知っていますが、誰が尋ねているのか、そしてbashとどの程度離れているのかを考慮することも好きです。非常に複雑な条件を提供した場合、彼らは何も学習せず、別の疑いがあるときはいつでも戻ってきます。私はもっ​​と理解しやすい答えを出すことを好みます。
fedorqui 'SO stop harming' 2014

7
へえ。まあ、学ぶ唯一の方法はたくさんの良いコードを読むことです。理解しやすいが、使用が推奨されていない偽のコードを与えると、それは教えるのに悪い方法です。また、bashの学習を始めたばかりの人(おそらく既に別の言語のいくつかのビットを知っている)はgrep-Eフラグ付きのコマンドよりも簡単に正規表現のbash構文を理解できると思います。
Aleks-Daniel Jakimenko-A。

8
@ Aleks-DanielJakimenko私はこの投稿をもう一度やりましたが、bash regexを使用するのが最善であることに同意します。良い方向を示してくれてありがとう、更新された答え。
fedorqui 'SO stop harming' 2014

4
...例えば、shのために、それをOPの質問よりも少し超えたを使用することができますUpvote、
Dereckson

3
@ Aleks-DanielJakimenkoがgrepを使用するのはshfishやその他のあまり装備されていないシェルを使用している場合に最適なオプションのようです。
tomekwi

47

bashバージョン3では、「=〜」演算子を使用できます。

if [[ "$date" =~ ^[0-9]{8}$ ]]; then
    echo "Valid date"
else
    echo "Invalid date"
fi

リファレンス:http : //tldp.org/LDP/abs/html/bashver3.html#REGEXMATCHREF

注:Bashバージョン3.2以降、二重括弧[[]]内の一致する演算子での引用は不要になりました。


20
通常の式でchar "を使用しないでください。式を使用すると機能しません
Dawid

さらに、{および}のバックスラッシュエスケープも同様に問題があります。
kbulgrien

32

文字列が正しい日付かどうかをテストする良い方法は、コマンドdateを使用することです:

if date -d "${DATE}" >/dev/null 2>&1
then
  # do what you need to do with your date
else
  echo "${DATE} incorrect date" >&2
  exit 1
fi

コメントから:書式設定を使用できます

if [ "2017-01-14" == $(date -d "2017-01-14" '+%Y-%m-%d') ] 

9
それは日付やないエラーが発生しやすいregexs'は日付関数の契約することができますとして非常にあなたの答えを評価
アリ

これは幅広い日付オプションを確認するのに適していますが、特定の日付形式を確認する必要がある場合は、それを実行できますか?たとえばdate -d 2017-11-14e、これを実行すると、2017年11月14日05:00:00 UTC 2017が返されますが、スクリプトが壊れてしまいます。
ジョサイア

1
次のようなものを使用できます:if ["2017-01-14" == $(date -d "2017-01-14" '+%Y-%m-%d')]日付が正しいかどうかをテストします結果が入力したデータと同じかどうかを確認します。ちなみに、ローカライズされた日付形式(たとえば、月-日-年対日-月-年)には十分注意してください
Django Janny

1
ロケールによっては、機能しない場合があります。MM-DD-YYYYを使用したアメリカの形式の日付はいずれかのDD-MM-YYYY(ヨーロッパ)またはYYYY-MM-DD(アジアではいくつかの場所)を使用して、世界のどこにも動作しません
ポール・

@Paul、何がうまくいかないかも?コメントに書かれているように、フォーマットオプションを使用できます...
Betlista

4

私はexpr match代わりに使用します=~

expr match "$date" "[0-9]\{8\}" >/dev/null && echo yes

これは、現在受け入れられている使用の回答よりも優れています。これは、空の文字列にも一致する=~ため=~です。仮定するbadvar定義されていない場合、[[ "1234" =~ "$badvar" ]]; echo $?(誤って)与え0ながら、expr match "1234" "$badvar" >/dev/null ; echo $?正しい結果を与えます1

を使用>/dev/nullしてexpr match出力値を非表示にする必要があります。これは、一致した文字数、または一致が見つからなかった場合は0です。その注意出力値が、その異なる終了ステータス。一致が見つかった場合の終了ステータスは0、そうでない場合の終了ステータスは1です。

一般的な構文exprは次のとおりです。

expr match "$string" "$lead"

または:

expr "$string" : "$lead"

どこ$lead正規表現です。exit status(これに名前はありますか?)leadの先頭スライスと一致する場合、その値はtrue(0)になりますstring。たとえば、expr match "abcdefghi" "abc"終了しますがtrueexpr match "abcdefghi" "bcd"終了しますfalse。(これを指摘してくれた@Carlo Woodへのクレジット。


7
=~は空の文字列と一致しません。指定した例では、空のパターンに対して文字列を一致しています。構文はstring =~ patternで、空のパターンはすべてに一致します。
bstpierre

2
これは部分文字列と一致せず、一致した先行文字の数を(stdoutに)返します。終了ステータスは、少なくとも1文字が一致した場合にtrueになります。これが、空の文字列(0文字に一致)の終了ステータスがfalseになる理由です。たとえば、expr match "abcdefghi" "^" && echo Matched || echo No match-とexpr match "abcdefghi" "bcd" && echo Matched || echo No match-の両方が返され"0\nNo match"ます。マッチング"a.*f"が戻るところ"6\nMatched"。したがって、例で '^'を使用することも不要であり、すでに暗示されています。
カルロウッド

@bstpierre:ここでのポイントは、=~一致する空の文字列の動作を合理化できるかどうかではありません。これは予期しない動作であり、エラーを引き起こす可能性があるということです。この回答は特に焦げたので書いたものです。
Penghe Geng

@PengheGeng予期しない動作?パターンに定義や制約がない場合、実際には何にも一致しません。パターンがないことは、すべてに一致します。堅牢なコードを書くことが答えであり、不十分な説明を正当化するものではありません。
アンソニー・ラトリッジ

@AnthonyRutledgeの「堅牢なコード」では、偶発的なコーディングエラーを防ぐために利用可能なツールを最大限に活用することが求められます。空の変数がスペルミスのような手段でいつでも簡単かつ偶発的に導入される可能性があるシェルコードでは、空の変数を一致させることは堅牢な機能だとは思いません。どうやらGNUの作者はexpr私に同意します。
Penghe Geng

0

正規表現の使用が日付の文字シーケンスが正しいかどうかを判断するのに役立つ場合、日付が有効かどうかを判断するために簡単に使用することはできません。次の例は正規表現を渡しますが、すべて無効な日付です:20180231、20190229、20190431

したがって、日付文字列(としましょうdatestr)が正しい形式かどうかを検証する場合は、それを解析して、文字列を正しい形式に変換するdateように依頼dateするのが最善です。両方の文字列が同一である場合、有効な形式と有効な日付があります。

if [[ "$datestr" == $(date -d "$datestr" "+%Y%m%d" 2>/dev/null) ]]; then
     echo "Valid date"
else
     echo "Invalid date"
fi
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.