Controlla se una stringa corrisponde a una regex nello script Bash


204

Uno degli argomenti che il mio script riceve è una data nel seguente formato: yyyymmdd.

Voglio verificare se ricevo una data valida come input.

Come posso fare questo? Sto cercando di usare un regex come:[0-9]\{\8}


Controllare se il formato è corretto è facile. Ma non penso che tu possa, in bash (con built-in), verificare se la data è valida.
RedX

Risposte:


317

È possibile utilizzare il costrutto test [[ ]], insieme all'operatore di corrispondenza delle espressioni regolari =~, per verificare se una stringa corrisponde a un modello regex.

Per il tuo caso specifico, puoi scrivere:

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

O più un test accurato:

[[ $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

Cioè, puoi definire una regex in Bash corrispondente al formato desiderato. In questo modo puoi fare:

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

dove i comandi after &&vengono eseguiti se il test ha esito positivo e i comandi after ||vengono eseguiti se il test ha esito negativo.

Nota che questo si basa sulla soluzione di Aleks-Daniel Jakimenko nella verifica del formato della data di input dell'utente in bash .


In altre shell puoi usare grep . Se la tua shell è conforme POSIX, fallo

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

Nel pesce , che non è conforme a POSIX, puoi farlo

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

19
Ne sono consapevole, ma mi piace anche prendere in considerazione chi lo sta chiedendo e quanto sono lontani da bash. Se forniamo condizioni molto complesse, non impareranno nulla e torneranno ogni volta che avranno un altro dubbio. Preferisco dare una risposta più comprensibile.
fedorqui "SO smettere di danneggiare" il

7
Eh. Bene, l'unico modo per imparare è leggere un sacco di buon codice. Se dai un codice falso che è facile da capire ma non è consigliabile usarlo, questo è un brutto modo di insegnare. Inoltre sono abbastanza sicuro che per coloro che hanno appena iniziato a imparare bash (probabilmente già conoscendo alcuni bit di un'altra lingua) capiranno la sintassi bash per regex più facilmente di qualche grepcomando con -Eflag.
Aleks-Daniel Jakimenko-A.

8
@ Aleks-DanielJakimenko Ho passato di nuovo questo post e ora sono d'accordo che è meglio usare bash regex. Grazie per indicare nella buona direzione, risposta aggiornata.
fedorqui "SO smettere di danneggiare",

4
Upvote, che consente di usarlo un po 'oltre la domanda OP, per esempio sh ...
Dereckson,

3
@ Aleks-DanielJakimenko usando grep sembra essere l'opzione migliore se stai usando sh, fisho altre shell meno equipaggiate.
Tomekwi,

47

Nella versione 3 di bash puoi usare l'operatore '= ~':

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

Riferimento: http://tldp.org/LDP/abs/html/bashver3.html#REGEXMATCHREF

NOTA: la quotazione nell'operatore corrispondente tra parentesi doppie, [[]], non è più necessaria dalla versione 3.2 di Bash


20
Non dovresti usare char "a espressione regolare? Perché quando uso expresion non funziona
Dawid Drozd

Inoltre, l'escursione della barra rovesciata di {e} è altrettanto problematica.
Kbulgrien,

32

Un buon modo per verificare se una stringa è una data corretta è utilizzare la data del comando:

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

dal commento: si può usare la formattazione

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

9
Valuta la tua risposta in quanto consente alla funzione di data di gestire le date e non le regex soggette a errori "
Ali,

Questo è utile per controllare le opzioni di data generali, ma se è necessario verificare un formato data specifico, può farlo? Ad esempio, se lo faccio date -d 2017-11-14erestituisce mar 14 nov 05:00:00 UTC 2017, ma ciò spezzerebbe il mio copione.
Giosia,

1
Potresti usare qualcosa del genere: if ["2017-01-14" == $ (date -d "2017-01-14" '+% Y-% m-% d')] Verifica se la data è corretta e controlla se il risultato è lo stesso dei tuoi dati inseriti. A proposito, stai molto attento con il formato della data localizzato (ad esempio mese-giorno-anno vs. giorno-mese-anno)
Django Janny

1
Potrebbe non funzionare, a seconda delle impostazioni locali. Le date in formato americano che utilizzano MM-GG-AAAA non funzioneranno in nessun'altra parte del mondo, utilizzando GG-MM-AAAA (Europa) o AAAA-MM-GG (alcuni posti in Asia)
Paul,

@Paul, cosa potrebbe non funzionare? Come scritto in un commento, si possono usare le opzioni di formattazione ...
Betlista,

4

Vorrei usare expr matchinvece di =~:

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

Questo è meglio della risposta attualmente accettata sull'uso =~perché =~corrisponderà anche a stringhe vuote, che IMHO non dovrebbe. Supponiamo che badvarnon sia definito, quindi [[ "1234" =~ "$badvar" ]]; echo $?fornisce (in modo errato) 0, mentre expr match "1234" "$badvar" >/dev/null ; echo $?fornisce un risultato corretto 1.

Dobbiamo usare >/dev/nullper nascondere expr matchil valore di output , che è il numero di caratteri corrispondenti o 0 se non viene trovata alcuna corrispondenza. Si noti che il suo valore di uscita è diverso dal suo stato di uscita . Lo stato di uscita è 0 se viene trovata una corrispondenza o 1 altrimenti.

Generalmente, la sintassi per exprè:

expr match "$string" "$lead"

O:

expr "$string" : "$lead"

dove $leadè un'espressione regolare. La sua exit statussarà vero (0) se leadpartite la fetta principale di string(C'è un nome per questo?). Ad esempio , expr match "abcdefghi" "abc"esce true, ma expr match "abcdefghi" "bcd"esce false. (Ringraziamo @Carlo Wood per averlo sottolineato.


7
=~non corrisponde a stringhe vuote, stai abbinando una stringa a un modello vuoto nell'esempio che dai. La sintassi è string =~ patterne un modello vuoto corrisponde a tutto.
bstpierre,

2
Questo non corrisponde a una stringa, restituisce (a stdout) il numero di importanti personaggi che ha trovato e lo stato di uscita è vera se e solo se almeno 1 personaggio è stato abbinato. Questo è il motivo per cui una stringa vuota (che corrisponde a 0 caratteri) ha uno stato di uscita falso. Ad esempio expr match "abcdefghi" "^" && echo Matched || echo No match- e expr match "abcdefghi" "bcd" && echo Matched || echo No match- entrambi ritornano "0\nNo match". Dove "a.*f"tornerà come corrispondenza "6\nMatched". L'uso del '^' nel tuo esempio è quindi anche superfluo e già implicito.
Carlo Wood,

@bstpierre: il punto qui non è se si può razionalizzare il comportamento della =~corrispondenza di stringhe vuote. È che questo comportamento potrebbe essere inaspettato e causare errori. Ho scritto questa risposta proprio perché ne sono stata bruciata.
Penghe Geng,

@PengheGeng Comportamento imprevisto? Se un modello non ha definizioni o vincoli, in realtà corrisponde a qualsiasi cosa. L'assenza di uno schema corrisponde a tutto. Scrivere un codice affidabile è la risposta, non giustificando una spiegazione scadente.
Anthony Rutledge,

@AnthonyRutledge "codice robusto" richiede il miglior utilizzo degli strumenti disponibili per prevenire errori di codifica accidentale. Nel codice Shell in cui una variabile vuota può essere facilmente e accidentalmente introdotta in qualsiasi momento tramite errori di ortografia, non credo che consentire la corrispondenza delle variabili vuote sia una funzionalità affidabile. Apparentemente l'autore di GNU è d' expraccordo con me.
Penghe Geng,

0

Laddove l'uso di una regex può essere utile per determinare se la sequenza di caratteri di una data è corretta, non può essere utilizzata facilmente per determinare se la data è valida. I seguenti esempi passeranno l'espressione regolare, ma sono tutte date non valide: 20180231, 20190229, 20190431

Quindi, se si desidera convalidare se la stringa della data (chiamiamola datestr) è nel formato corretto, è meglio analizzarla datee chiedere datedi convertire la stringa nel formato corretto. Se entrambe le stringhe sono identiche, hai un formato e una data validi.

if [[ "$datestr" == $(date -d "$datestr" "+%Y%m%d" 2>/dev/null) ]]; then
     echo "Valid date"
else
     echo "Invalid date"
fi
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.