Jednym z argumentów, że mój skrypt otrzymuje to data w następującym formacie: yyyymmdd
.
Chcę sprawdzić, czy otrzymam prawidłową datę jako dane wejściowe.
W jaki sposób mogę to zrobić? Próbuję użyć wyrażenia regularnego, takiego jak:[0-9]\{\8}
Jednym z argumentów, że mój skrypt otrzymuje to data w następującym formacie: yyyymmdd
.
Chcę sprawdzić, czy otrzymam prawidłową datę jako dane wejściowe.
W jaki sposób mogę to zrobić? Próbuję użyć wyrażenia regularnego, takiego jak:[0-9]\{\8}
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ć:
[[ $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.
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
(echo "$date" | grep -Eq ^regex$) && echo "matched" || echo "did not match"
W przypadku ryb , które nie są zgodne z POSIX, możesz to zrobić
echo "$date" | grep -Eq "^regex\$"; and echo "matched"; or echo "did not match"
grep
polecenia z -E
flagą.
sh
, fish
lub innych mniej wyposażone muszle.
W wersji bash 3 możesz użyć operatora „= ~”:
if [[ "$date" =~ ^[0-9]{8}$ ]]; then
echo "Valid date"
else
echo "Invalid date"
fi
Odniesienie: http://tldp.org/LDP/abs/html/bashver3.html#REGEXMATCHREF
UWAGA: Cytowanie w operatorze dopasowywania w podwójnych nawiasach [[]] nie jest już konieczne od wersji Bash 3.2
Dobrym sposobem sprawdzenia, czy ciąg znaków jest poprawną datą, jest użycie polecenia 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
z komentarza: można użyć formatowania
if [ "2017-01-14" == $(date -d "2017-01-14" '+%Y-%m-%d') ]
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 =~
:
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 badvar
nie 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/null
do 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 $lead
jest wyrażenie regularne. To exit status
będzie prawda (0), jeśli lead
pasuje 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.
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.
=~
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ę.
if [[ "$datestr" == $(date -d "$datestr" "+%Y%m%d" 2>/dev/null) ]]; then
echo "Valid date"
else
echo "Invalid date"
fi