Es ist einfach zu überprüfen, ob das Format stimmt. Aber ich glaube nicht, dass Sie in Bash (mit eingebauten) überprüfen können, ob das Datum gültig ist.
RedX
Antworten:
315
Sie können das Testkonstrukt [[ ]]zusammen mit dem Übereinstimmungsoperator für reguläre Ausdrücke verwenden =~, um zu überprüfen, ob eine Zeichenfolge mit einem Regex-Muster übereinstimmt.
Für Ihren speziellen Fall können Sie schreiben:
[[ $date =~^[0-9]{8}$ ]]&& echo "yes"
Oder genauer gesagt ein genauer Test:
[[ $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
Das heißt, Sie können in Bash einen regulären Ausdruck definieren, der dem gewünschten Format entspricht. So können Sie vorgehen:
[[ $date =~^regex$ ]]&& echo "matched"|| echo "did not match"
Dabei werden Befehle nach &&ausgeführt, wenn der Test erfolgreich ist, und Befehle nach ||ausgeführt, wenn der Test nicht erfolgreich ist.
Ich bin mir dessen bewusst, aber ich berücksichtige auch gerne, wer fragt und wie weit sie mit Bash sind. Wenn wir sehr komplexe Bedingungen bieten, werden sie nichts lernen und einfach zurückkommen, wenn sie einen anderen Zweifel haben. Ich gebe lieber eine verständlichere Antwort.
Fedorqui 'SO hör auf,'
7
Heh. Der einzige Weg zu lernen ist, viel guten Code zu lesen. Wenn Sie falschen Code angeben, der leicht zu verstehen ist, dessen Verwendung jedoch nicht empfohlen wird, ist dies eine schlechte Art zu unterrichten. Ich bin mir auch ziemlich sicher, dass diejenigen, die gerade erst angefangen haben, Bash zu lernen (wahrscheinlich bereits einige Teile einer anderen Sprache kennen), die Bash-Syntax für Regex leichter verstehen werden als einige grepBefehle mit -EFlag.
Aleks-Daniel Jakimenko-A.
8
@ Aleks-DanielJakimenko Ich habe diesen Beitrag noch einmal durchgesehen und jetzt stimme ich zu, dass es am besten ist, Bash Regex zu verwenden. Vielen Dank für den Hinweis in die gute Richtung, aktualisierte Antwort.
Fedorqui 'SO hör auf zu schaden'
4
Upvote, das erlaubt es, es ein wenig jenseits der OP-Frage zu verwenden, zum Beispiel für sh ..
Dereckson
3
@ Aleks-DanielJakimenko grep scheint die beste Option zu sein , wenn Sie verwenden sh, fishoder andere weniger gut ausgestattete Muscheln.
Tomékwi
47
In Bash Version 3 können Sie den Operator '= ~' verwenden:
Bewerten Sie Ihre Antwort hoch, da die Datumsfunktion die Datumsangaben und nicht die fehleranfälligen regulären Ausdrücke verarbeiten kann
Ali
Dies ist gut, um allgemeine Datumsoptionen zu überprüfen. Wenn Sie jedoch ein bestimmtes Datumsformat überprüfen müssen, kann dies durchgeführt werden? Wenn ich date -d 2017-11-14ees zum Beispiel mache, wird es am 14. November um 05:00:00 UTC 2017 zurückgegeben, aber das würde mein Skript beschädigen.
Josiah
1
Sie könnten so etwas verwenden: if ["2017-01-14" == $ (Datum -d "2017-01-14" '+% Y-% m-% d')] Es wird geprüft, ob das Datum korrekt ist und prüfen Sie, ob das Ergebnis mit Ihren eingegebenen Daten übereinstimmt. Übrigens, seien Sie sehr vorsichtig mit dem lokalisierten Datumsformat (zum Beispiel Monat-Tag-Jahr vs. Tag-Monat-Jahr)
Django Janny
1
Funktioniert möglicherweise nicht, abhängig von Ihrem Gebietsschema. Amerikanisch formatierte Daten mit MM-TT-JJJJ funktionieren nirgendwo anders auf der Welt, entweder mit TT-MM-JJJJ (Europa) oder JJJJ-MM-TT (einige Orte in Asien)
Paul
@ Paul, was kann nicht funktionieren? Wie in einem Kommentar geschrieben, kann man Formatierungsoptionen verwenden ...
Betlista
4
Ich würde expr matchanstelle von verwenden =~:
expr match "$date""[0-9]\{8\}">/dev/null && echo yes
Dies ist besser als die derzeit akzeptierte Antwort der Verwendung, =~da =~sie auch mit leeren Zeichenfolgen übereinstimmt, was meiner Meinung nach nicht der Fall sein sollte. Angenommen, es badvarist nicht definiert, dann [[ "1234" =~ "$badvar" ]]; echo $?gibt es (falsch) 0, während es expr match "1234" "$badvar" >/dev/null ; echo $?das richtige Ergebnis gibt 1.
Wir müssen den Ausgabewert>/dev/null ausblenden expr match, dh die Anzahl der übereinstimmenden Zeichen oder 0, wenn keine Übereinstimmung gefunden wurde. Beachten Sie, dass sich der Ausgabewert vom Exit-Status unterscheidet . Der Exit-Status ist 0, wenn eine Übereinstimmung gefunden wurde, oder 1, wenn dies nicht der Fall ist.
Im Allgemeinen exprlautet die Syntax für :
expr match "$string""$lead"
Oder:
expr "$string":"$lead"
wo $leadist ein regulärer Ausdruck. Es exit statusist wahr (0), wenn es leadmit dem führenden Slice von string(Gibt es einen Namen dafür?) Übereinstimmt . Zum Beispiel expr match "abcdefghi" "abc"Exits true, aber expr match "abcdefghi" "bcd"Exits false. (Dank an @Carlo Wood für den Hinweis.
=~stimmt nicht mit leeren Zeichenfolgen überein, Sie vergleichen eine Zeichenfolge mit einem leeren Muster in dem von Ihnen angegebenen Beispiel. Die Syntax lautet string =~ patternund ein leeres Muster passt zu allem.
Bstpierre
2
Dies stimmt nicht mit einer Teilzeichenfolge überein, sondern gibt (auf stdout) die Anzahl der führenden Zeichen zurück, die übereinstimmen, und der Beendigungsstatus ist wahr, wenn mindestens 1 Zeichen übereinstimmt. Aus diesem Grund hat eine leere Zeichenfolge (die 0 Zeichen entspricht) den Beendigungsstatus false. Zum Beispiel expr match "abcdefghi" "^" && echo Matched || echo No match- und expr match "abcdefghi" "bcd" && echo Matched || echo No match- kehren beide zurück "0\nNo match". Wo als Matching "a.*f"wird zurückkehren "6\nMatched". Die Verwendung des '^' in Ihrem Beispiel ist daher ebenfalls unnötig und bereits impliziert.
Carlo Wood
@bstpierre: Hier geht es nicht darum, ob man das Verhalten beim =~Abgleichen leerer Strings rationalisieren kann . Es ist so, dass dieses Verhalten unerwartet sein und Fehler verursachen kann. Ich habe diese Antwort speziell geschrieben, weil ich davon verbrannt wurde.
Penghe Geng
@PengheGeng Unerwartetes Verhalten? Wenn ein Muster keine Definition oder Einschränkungen hat, stimmt es tatsächlich mit allem überein. Das Fehlen eines Musters passt zu allem. Das Schreiben von robustem Code ist die Antwort und rechtfertigt keine schlechte Erklärung.
Anthony Rutledge
@AnthonyRutledge "robuster Code" erfordert die bestmögliche Verwendung der verfügbaren Tools, um versehentliche Codierungsfehler zu vermeiden. In Shell-Code, in dem eine leere Variable jederzeit leicht und versehentlich durch Rechtschreibfehler eingeführt werden kann, halte ich das Abgleichen leerer Variablen nicht für eine robuste Funktion. Anscheinend exprstimmt der Autor von GNU mir zu.
Penghe Geng
0
Wenn die Verwendung eines regulären Ausdrucks hilfreich sein kann, um festzustellen, ob die Zeichenfolge eines Datums korrekt ist, kann sie nicht einfach verwendet werden, um festzustellen, ob das Datum gültig ist. Die folgenden Beispiele übergeben den regulären Ausdruck, sind jedoch alle ungültige Daten: 20180231, 20190229, 20190431
Wenn Sie also überprüfen möchten, ob Ihre Datumszeichenfolge (nennen wir sie datestr) das richtige Format hat, ist es am besten, sie zu analysieren dateund datedie Zeichenfolge in das richtige Format zu konvertieren. Wenn beide Zeichenfolgen identisch sind, haben Sie ein gültiges Format und ein gültiges Datum.
Antworten:
Sie können das Testkonstrukt
[[ ]]
zusammen mit dem Übereinstimmungsoperator für reguläre Ausdrücke verwenden=~
, um zu überprüfen, ob eine Zeichenfolge mit einem Regex-Muster übereinstimmt.Für Ihren speziellen Fall können Sie schreiben:
Oder genauer gesagt ein genauer Test:
Das heißt, Sie können in Bash einen regulären Ausdruck definieren, der dem gewünschten Format entspricht. So können Sie vorgehen:
Dabei werden Befehle nach
&&
ausgeführt, wenn der Test erfolgreich ist, und Befehle nach||
ausgeführt, wenn der Test nicht erfolgreich ist.Beachten Sie, dass dies auf der Lösung von Aleks-Daniel Jakimenko in der Überprüfung des Datumsformats für Benutzereingaben in Bash basiert .
In anderen Shells können Sie grep verwenden . Wenn Ihre Shell POSIX-kompatibel ist, tun Sie dies
Bei Fischen , die nicht POSIX-konform sind, können Sie dies tun
quelle
grep
Befehle mit-E
Flag.sh
,fish
oder andere weniger gut ausgestattete Muscheln.In Bash Version 3 können Sie den Operator '= ~' verwenden:
Referenz: http://tldp.org/LDP/abs/html/bashver3.html#REGEXMATCHREF
quelle
Eine gute Möglichkeit, um zu testen, ob eine Zeichenfolge ein korrektes Datum ist, besteht darin, das Befehlsdatum zu verwenden:
Aus Kommentar: Man kann die Formatierung verwenden
quelle
date -d 2017-11-14e
es zum Beispiel mache, wird es am 14. November um 05:00:00 UTC 2017 zurückgegeben, aber das würde mein Skript beschädigen.Ich würde
expr match
anstelle von verwenden=~
:Dies ist besser als die derzeit akzeptierte Antwort der Verwendung,
=~
da=~
sie auch mit leeren Zeichenfolgen übereinstimmt, was meiner Meinung nach nicht der Fall sein sollte. Angenommen, esbadvar
ist nicht definiert, dann[[ "1234" =~ "$badvar" ]]; echo $?
gibt es (falsch)0
, während esexpr match "1234" "$badvar" >/dev/null ; echo $?
das richtige Ergebnis gibt1
.Wir müssen den Ausgabewert
>/dev/null
ausblendenexpr match
, dh die Anzahl der übereinstimmenden Zeichen oder 0, wenn keine Übereinstimmung gefunden wurde. Beachten Sie, dass sich der Ausgabewert vom Exit-Status unterscheidet . Der Exit-Status ist 0, wenn eine Übereinstimmung gefunden wurde, oder 1, wenn dies nicht der Fall ist.Im Allgemeinen
expr
lautet die Syntax für :Oder:
wo
$lead
ist ein regulärer Ausdruck. Esexit status
ist wahr (0), wenn eslead
mit dem führenden Slice vonstring
(Gibt es einen Namen dafür?) Übereinstimmt . Zum Beispielexpr match "abcdefghi" "abc"
Exitstrue
, aberexpr match "abcdefghi" "bcd"
Exitsfalse
. (Dank an @Carlo Wood für den Hinweis.quelle
=~
stimmt nicht mit leeren Zeichenfolgen überein, Sie vergleichen eine Zeichenfolge mit einem leeren Muster in dem von Ihnen angegebenen Beispiel. Die Syntax lautetstring =~ pattern
und ein leeres Muster passt zu allem.expr match "abcdefghi" "^" && echo Matched || echo No match
- undexpr match "abcdefghi" "bcd" && echo Matched || echo No match
- kehren beide zurück"0\nNo match"
. Wo als Matching"a.*f"
wird zurückkehren"6\nMatched"
. Die Verwendung des '^' in Ihrem Beispiel ist daher ebenfalls unnötig und bereits impliziert.=~
Abgleichen leerer Strings rationalisieren kann . Es ist so, dass dieses Verhalten unerwartet sein und Fehler verursachen kann. Ich habe diese Antwort speziell geschrieben, weil ich davon verbrannt wurde.expr
stimmt der Autor von GNU mir zu.Wenn die Verwendung eines regulären Ausdrucks hilfreich sein kann, um festzustellen, ob die Zeichenfolge eines Datums korrekt ist, kann sie nicht einfach verwendet werden, um festzustellen, ob das Datum gültig ist. Die folgenden Beispiele übergeben den regulären Ausdruck, sind jedoch alle ungültige Daten: 20180231, 20190229, 20190431
Wenn Sie also überprüfen möchten, ob Ihre Datumszeichenfolge (nennen wir sie
datestr
) das richtige Format hat, ist es am besten, sie zu analysierendate
unddate
die Zeichenfolge in das richtige Format zu konvertieren. Wenn beide Zeichenfolgen identisch sind, haben Sie ein gültiges Format und ein gültiges Datum.quelle