Sprawdzanie poprawności formatu jest łatwe. Ale nie sądzę, że można w bash (z wbudowanymi) sprawdzić, czy data jest poprawna.
RedX
Odpowiedzi:
316
Możesz użyć konstrukcji testowej [[ ]]wraz z operatorem dopasowania wyrażeń regularnych =~, aby sprawdzić, czy łańcuch pasuje do wzorca wyrażenia regularnego.
W konkretnym przypadku możesz napisać:
[[ $date =~^[0-9]{8}$ ]]&& echo "yes"
Lub dokładniejszy 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
Oznacza to, że możesz zdefiniować wyrażenie regularne w Bash pasujące do żądanego formatu. W ten sposób możesz:
[[ $date =~^regex$ ]]&& echo "matched"|| echo "did not match"
gdzie polecenia po &&są wykonywane, jeśli test się powiedzie, a polecenia po ||są wykonywane, jeśli test się nie powiedzie.
Jestem tego świadomy, ale lubię też brać pod uwagę, kto pyta i jak daleko jest do bashu. Jeśli zapewnimy bardzo skomplikowane warunki, nie nauczą się niczego i po prostu wrócą, gdy będą mieli inne wątpliwości. Wolę dać bardziej zrozumiałą odpowiedź.
fedorqui „SO przestań krzywdzić”
7
Heh Cóż, jedynym sposobem na naukę jest przeczytanie dużo dobrego kodu. Jeśli podajesz fałszywy kod, który jest łatwy do zrozumienia, ale nie jest zalecany do użycia - to zły sposób na naukę. Jestem też całkiem pewien, że dla tych, którzy dopiero zaczęli uczyć się bash (prawdopodobnie już znając niektóre bity innego języka), zrozumieją składnię bash dla wyrażeń regularnych łatwiej niż jakieś greppolecenia z -Eflagą.
Aleks-Daniel Jakimenko-A.
8
@ Aleks-DanielJakimenko Ponownie przejrzałem ten post i teraz zgadzam się, że najlepiej jest użyć wyrażenia regularnego bash. Dziękujemy za wskazanie dobrego kierunku, zaktualizowana odpowiedź.
fedorqui „SO przestań szkodzić”
4
Głosowanie, które pozwala użyć go nieco poza pytaniem OP, na przykład sh.
Dereckson,
3
@ Aleks-DanielJakimenko używając grep wydaje się być najlepszym rozwiązaniem, jeśli używasz sh, fishlub innych mniej wyposażone muszle.
Zdecydowanie oceń swoją odpowiedź, ponieważ pozwala funkcji daty radzić sobie z datami, a nie podatnymi na błędy wyrażeniami regularnymi
Ali
Przydaje się to do sprawdzania szerokich opcji dat, ale czy musisz to sprawdzić? Na przykład, jeśli date -d 2017-11-14eto zrobię , zwróci wtorek 14 listopada 05:00:00 UTC 2017, ale to zepsułoby mój skrypt.
Josiah
1
Możesz użyć czegoś takiego: jeśli ["2017-01-14" == $ (data -d "2017-01-14" '+% Y-% m-% d')] Sprawdza, czy data jest poprawna i sprawdź, czy wynik jest taki sam, jak wprowadzone dane. Nawiasem mówiąc, bądź bardzo ostrożny ze zlokalizowanym formatem daty (na przykład Miesiąc-Dzień-Rok vs. Dzień-Miesiąc-Rok)
Django Janny
1
Może nie działać, w zależności od lokalizacji. Amerykańskie daty sformatowane przy użyciu MM-DD-RRRR nie będą działać nigdzie indziej na świecie, przy użyciu DD-MM-RRRR (Europa) lub RRRR-MM-DD (niektóre miejsca w Azji)
Paul
@Paul, co może nie działać? Jak napisano w komentarzu, można użyć opcji formatowania ...
Betlista
4
Chciałbym użyć expr matchzamiast =~:
expr match "$date""[0-9]\{8\}">/dev/null && echo yes
Jest to lepsze niż obecnie akceptowana odpowiedź na użycie, =~ponieważ =~dopasuje również puste ciągi, których IMHO nie powinno. Załóżmy, że badvarnie jest zdefiniowany, a następnie [[ "1234" =~ "$badvar" ]]; echo $?podaje (niepoprawnie) 0, a expr match "1234" "$badvar" >/dev/null ; echo $?daje poprawny wynik 1.
Musimy wykorzystać >/dev/nulldo ukrycia expr match„s wartości wyjściowej , która jest dopasowana liczba znaków lub 0 jeśli znaleziono żadnego meczu. Uwaga: jego wartość wyjściowa różni się od statusu wyjścia . Status wyjścia wynosi 0, jeśli znaleziono dopasowanie, lub 1 w innym przypadku.
Ogólnie rzecz biorąc, składnia dla expr:
expr match "$string""$lead"
Lub:
expr "$string":"$lead"
gdzie $leadjest wyrażenie regularne. To exit statusbędzie prawda (0), jeśli leadpasuje do wiodącego wycinka string(Czy istnieje na to nazwa?). Na przykład expr match "abcdefghi" "abc"wychodzi true, ale expr match "abcdefghi" "bcd"wychodzi false. (Podziękowania dla @Carlo Wood za zwrócenie na to uwagi.
=~nie pasuje do pustych ciągów, dopasowujesz ciąg do pustego wzorca w podanym przykładzie. Składnia jest string =~ pattern, a pusty wzorzec pasuje do wszystkiego.
bstpierre
2
To nie pasuje do podłańcucha, zwraca (na standardowe wyjście) liczbę pasujących znaków wiodących , a status wyjścia jest prawdziwy, jeśli dopasowano co najmniej 1 znak. Dlatego pusty łańcuch (który pasuje do 0 znaków) ma status wyjścia false. Na przykład expr match "abcdefghi" "^" && echo Matched || echo No match- i expr match "abcdefghi" "bcd" && echo Matched || echo No match- oba wracają "0\nNo match". Gdzie jako pasujące "a.*f"powróci "6\nMatched". Użycie „^” w twoim przykładzie jest zatem również niepotrzebne i już sugerowane.
Carlo Wood
@bstpierre: tutaj nie chodzi o to, czy można zracjonalizować zachowanie =~pasujących pustych ciągów. Chodzi o to, że takie zachowanie może być nieoczekiwane i powodować błędy. Napisałem tę odpowiedź specjalnie dlatego, że ją spaliłem.
Penghe Geng
@PengheGeng Nieoczekiwane zachowanie? Jeśli wzorzec nie ma definicji ani ograniczeń, to w rzeczywistości pasuje do czegokolwiek. Brak wzoru pasuje do wszystkiego. Pisanie solidnego kodu jest odpowiedzią, nie uzasadniającą złego wyjaśnienia.
Anthony Rutledge
@AnthonyRutledge „solidny kod” wymaga najlepszego wykorzystania dostępnych narzędzi, aby zapobiec przypadkowym błędom kodowania. W kodzie Shell, w którym pustą zmienną można łatwo i przypadkowo wprowadzić w dowolnym momencie za pomocą błędów ortograficznych, nie sądzę, aby zezwolenie na dopasowanie pustych zmiennych było solidną funkcją. Najwyraźniej autor GNU exprzgadza się ze mną.
Penghe Geng
0
Jeśli użycie wyrażenia regularnego może być pomocne w ustaleniu, czy sekwencja znaków daty jest poprawna, nie można go łatwo użyć do ustalenia, czy data jest poprawna. Poniższe przykłady przekażą wyrażenie regularne, ale wszystkie są niepoprawnymi datami: 20180231, 20190229, 20190431
Jeśli więc chcesz sprawdzić, czy ciąg daty (nazwijmy go datestr) ma poprawny format, najlepiej go przeanalizować datei poprosić dateo konwersję ciągu do właściwego formatu. Jeśli oba ciągi są identyczne, masz prawidłowy format i prawidłową datę.
Odpowiedzi:
Możesz użyć konstrukcji testowej
[[ ]]
wraz z operatorem dopasowania wyrażeń regularnych=~
, aby sprawdzić, czy łańcuch pasuje do wzorca wyrażenia regularnego.W konkretnym przypadku możesz napisać:
Lub dokładniejszy test:
Oznacza to, że możesz zdefiniować wyrażenie regularne w Bash pasujące do żądanego formatu. W ten sposób możesz:
gdzie polecenia po
&&
są wykonywane, jeśli test się powiedzie, a polecenia po||
są wykonywane, jeśli test się nie powiedzie.Zauważ, że jest to oparte na rozwiązaniu Aleksa-Daniela Jakimenko w weryfikacji formatu daty wejściowej użytkownika w bash .
W innych powłokach możesz użyć grep . Jeśli twoja powłoka jest zgodna z POSIX, zrób
W przypadku ryb , które nie są zgodne z POSIX, możesz to zrobić
źródło
grep
polecenia z-E
flagą.sh
,fish
lub innych mniej wyposażone muszle.W wersji bash 3 możesz użyć operatora „= ~”:
Odniesienie: http://tldp.org/LDP/abs/html/bashver3.html#REGEXMATCHREF
źródło
Dobrym sposobem sprawdzenia, czy ciąg znaków jest poprawną datą, jest użycie polecenia date:
z komentarza: można użyć formatowania
źródło
date -d 2017-11-14e
to zrobię , zwróci wtorek 14 listopada 05:00:00 UTC 2017, ale to zepsułoby mój skrypt.Chciałbym użyć
expr match
zamiast=~
:Jest to lepsze niż obecnie akceptowana odpowiedź na użycie,
=~
ponieważ=~
dopasuje również puste ciągi, których IMHO nie powinno. Załóżmy, żebadvar
nie jest zdefiniowany, a następnie[[ "1234" =~ "$badvar" ]]; echo $?
podaje (niepoprawnie)0
, aexpr match "1234" "$badvar" >/dev/null ; echo $?
daje poprawny wynik1
.Musimy wykorzystać
>/dev/null
do ukryciaexpr match
„s wartości wyjściowej , która jest dopasowana liczba znaków lub 0 jeśli znaleziono żadnego meczu. Uwaga: jego wartość wyjściowa różni się od statusu wyjścia . Status wyjścia wynosi 0, jeśli znaleziono dopasowanie, lub 1 w innym przypadku.Ogólnie rzecz biorąc, składnia dla
expr
:Lub:
gdzie
$lead
jest wyrażenie regularne. Toexit status
będzie prawda (0), jeślilead
pasuje do wiodącego wycinkastring
(Czy istnieje na to nazwa?). Na przykładexpr match "abcdefghi" "abc"
wychodzitrue
, aleexpr match "abcdefghi" "bcd"
wychodzifalse
. (Podziękowania dla @Carlo Wood za zwrócenie na to uwagi.źródło
=~
nie pasuje do pustych ciągów, dopasowujesz ciąg do pustego wzorca w podanym przykładzie. Składnia jeststring =~ pattern
, a pusty wzorzec pasuje do wszystkiego.expr match "abcdefghi" "^" && echo Matched || echo No match
- iexpr match "abcdefghi" "bcd" && echo Matched || echo No match
- oba wracają"0\nNo match"
. Gdzie jako pasujące"a.*f"
powróci"6\nMatched"
. Użycie „^” w twoim przykładzie jest zatem również niepotrzebne i już sugerowane.=~
pasujących pustych ciągów. Chodzi o to, że takie zachowanie może być nieoczekiwane i powodować błędy. Napisałem tę odpowiedź specjalnie dlatego, że ją spaliłem.expr
zgadza się ze mną.Jeśli użycie wyrażenia regularnego może być pomocne w ustaleniu, czy sekwencja znaków daty jest poprawna, nie można go łatwo użyć do ustalenia, czy data jest poprawna. Poniższe przykłady przekażą wyrażenie regularne, ale wszystkie są niepoprawnymi datami: 20180231, 20190229, 20190431
Jeśli więc chcesz sprawdzić, czy ciąg daty (nazwijmy go
datestr
) ma poprawny format, najlepiej go przeanalizowaćdate
i poprosićdate
o konwersję ciągu do właściwego formatu. Jeśli oba ciągi są identyczne, masz prawidłowy format i prawidłową datę.źródło