Corrispondenza con espressioni regolari in un'istruzione if di Bash


89

Cosa ho sbagliato qui?

Tentativo di trovare una qualsiasi stringa che contiene spazi, lettere minuscole, maiuscole o numeri. Anche i caratteri speciali sarebbero carini, ma penso che ciò richieda la fuga di determinati caratteri.

TEST="THIS is a TEST title with some numbers 12345 and special char *&^%$#"

if [[ "$TEST" =~ [^a-zA-Z0-9\ ] ]]; then BLAH; fi

Questo ovviamente verifica solo le lettere maiuscole, minuscole, numeri e spazi. Non funziona però.

* AGGIORNARE *

Immagino che avrei dovuto essere più specifico. Ecco l'effettiva riga di codice reale.

if [[ "$TITLE" =~ [^a-zA-Z0-9\ ] ]]; then RETURN="FAIL" && ERROR="ERROR: Title can only contain upper and lowercase letters, numbers, and spaces!"; fi

* AGGIORNARE *

./anm.sh: line 265: syntax error in conditional expression
./anm.sh: line 265: syntax error near `&*#]'
./anm.sh: line 265: `  if [[ ! "$TITLE" =~ [a-zA-Z0-9 $%^\&*#] ]]; then RETURN="FAIL" && ERROR="ERROR: Title can only contain upper and lowercase letters, numbers, and spaces!"; return; fi'

Quale shell stai effettivamente utilizzando? / bin / sh? / bin / bash? / bin / csh?
Willem Van Onsem

8
È più sicuro inserire la regex in una variabile. re='...whatever...'; [[ $string =~ $re ]](senza virgolette: questo è uno dei rari casi in cui rompono qualcosa che funzionerebbe senza di loro).
Charles Duffy

3
Metti invece virgolette singole attorno al compito. Le virgolette doppie non proteggeranno adeguatamente i caratteri speciali.
tripleee

Molte grazie Charles! Va ancora bene non metterlo in una variabile, ma NON deve essere citato affatto! Ad esempio: [[ $var =~ .* ]]per corrispondenza regex .*(qualsiasi cosa). Immagino che se usi le virgolette, le virgolette stesse sono considerate parte della regex ...
Stéphane

4
Riepilogo gotcha Ho trovato: (1.) salva il pattern in una variabile utilizzando virgolette singole pattern='^hello[0-9]*$'(2.) nell'espressione a doppio quadrato se hai bisogno della corrispondenza regex NON citare il pattern perché citando DISABILITA la corrispondenza del pattern regex. (cioè l'espressione [[ "$x" =~ $pattern ]]corrisponderà utilizzando regex e l'espressione [[ "$x" =~ "$pattern" ]]disabilita la corrispondenza regex ed è equivalente a[[ "$x" == "$pattern" ]] ).
Trevor Boyd Smith

Risposte:


184

Ci sono un paio di cose importanti da sapere sulla [[ ]]costruzione di bash . Il primo:

La suddivisione delle parole e l'espansione del nome del percorso non vengono eseguite sulle parole comprese tra [[e ]]; Vengono eseguite l'espansione della tilde, l'espansione di parametri e variabili, l'espansione aritmetica, la sostituzione dei comandi, la sostituzione dei processi e la rimozione delle quote.

La seconda cosa:

È disponibile un operatore binario aggiuntivo, '= ~', ... la stringa a destra dell'operatore è considerata un'espressione regolare estesa e corrisponde di conseguenza ... Qualsiasi parte del modello può essere citata per forzarne la corrispondenza come una stringa .

Di conseguenza, $vsu entrambi i lati di =~verrà espanso al valore di quella variabile, ma il risultato non sarà suddiviso in parole o espanso nel nome del percorso. In altre parole, è perfettamente sicuro lasciare le espansioni variabili non quotate sul lato sinistro, ma è necessario sapere che le espansioni variabili avverranno sul lato destro.

Quindi se scrivi :, [[ $x =~ [$0-9a-zA-Z] ]]l' $0interno della regex a destra verrà espanso prima che la regex venga interpretata, il che probabilmente causerà la mancata compilazione dell'espressione regolare (a meno che l'espansione della regex non termini $0con una cifra o un simbolo di punteggiatura il cui valore ascii sia inferiore a una cifra). Se si cita il lato destro in questo modo [[ $x =~ "[$0-9a-zA-Z]" ]], il lato destro verrà trattato come una stringa normale, non come un'espressione$0 regolare (e sarà comunque espanso). Quello che vuoi veramente in questo caso è[[ $x =~ [\$0-9a-zA-Z] ]]

Allo stesso modo, l'espressione tra [[e ]]viene suddivisa in parole prima che la regex venga interpretata. Quindi gli spazi nella regex devono essere preceduti da caratteri di escape o citati. Se si voleva abbinare lettere, cifre o spazi si potrebbe usare: [[ $x =~ [0-9a-zA-Z\ ] ]]. Allo stesso modo è necessario eseguire l'escape di altri caratteri, come #, che inizierebbe un commento se non citato. Ovviamente puoi inserire il pattern in una variabile:

pat="[0-9a-zA-Z ]"
if [[ $x =~ $pat ]]; then ...

Per le regex che contengono molti caratteri che dovrebbero essere sottoposti a escape o citati per passare attraverso il lexer di bash, molte persone preferiscono questo stile. Ma attenzione: in questo caso, non puoi citare l'espansione della variabile:

# This doesn't work:
if [[ $x =~ "$pat" ]]; then ...

Infine, penso che quello che stai cercando di fare sia verificare che la variabile contenga solo caratteri validi. Il modo più semplice per eseguire questo controllo è assicurarsi che non contenga un carattere non valido. In altre parole, un'espressione come questa:

valid='0-9a-zA-Z $%&#' # add almost whatever else you want to allow to the list
if [[ ! $x =~ [^$valid] ]]; then ...

!nega il test, trasformandolo in un operatore "non corrisponde" e una [^...]classe di caratteri regex significa "qualsiasi carattere diverso da ...".

La combinazione dell'espansione dei parametri e degli operatori regex può rendere la sintassi delle espressioni regolari bash "quasi leggibile", ma ci sono ancora alcuni trucchi. (Non Ci sono sempre?) La prima è che non si poteva mettere ]in $valid, anche se $validsono stati citati, ad eccezione proprio all'inizio. (Questa è una regola regex di Posix: se vuoi includere ]in una classe di caratteri, deve andare all'inizio. -Può andare all'inizio o alla fine, quindi se hai bisogno di entrambi ]e -, devi iniziare ]e finire con -, che porta alla espressione regolare "io so quello che sto facendo" emoticon: [][-])


6
Voglio solo sottolineare che "! ~ È l'operatore" non corrisponde "" non è vero. Utilizzare if ! [[ $x =~ $y ]]oif [[ ! $x =~ $y ]]
alcol

shellchecker non è d'accordo ...SC2076: Don't quote rhs of =~, it'll match literally rather than as a regex.
Leonardo

4
@leonard: in che cosa differisce dalla mia affermazione "non puoi citare l'espansione della variabile" e dal commento "Questo non funziona"? Cosa non è chiaro a riguardo?
rici

1
@jinbeomhong: l'espressione stessa è separata in parole come al solito, utilizzando spazi bianchi. Ma le espansioni di parametri e comandi non sono suddivise in parole.
rici

1
@jinbeomhong: non sto dicendo nulla di diverso dal manuale di bash. "le parole tra [[e ]]" vengono analizzate dal testo del programma, allo stesso modo in cui le righe di comando vengono analizzate in parole. A differenza delle righe di comando, tuttavia, le parole non vengono divise dopo le espansioni.
rici

30

Nel caso qualcuno volesse un esempio utilizzando le variabili ...

#!/bin/bash

# Only continue for 'develop' or 'release/*' branches
BRANCH_REGEX="^(develop$|release//*)"

if [[ $BRANCH =~ $BRANCH_REGEX ]];
then
    echo "BRANCH '$BRANCH' matches BRANCH_REGEX '$BRANCH_REGEX'"
else
    echo "BRANCH '$BRANCH' DOES NOT MATCH BRANCH_REGEX '$BRANCH_REGEX'"
fi

13

Preferisco usare [:punct:]per quello. Inoltre, a-zA-Z09-9potrebbe essere solo [:alnum:]:

[[ $TEST =~ ^[[:alnum:][:blank:][:punct:]]+$ ]]

-1

Oppure potresti guardare questa domanda perché ti è capitato di fare uno stupido errore di battitura come ho fatto io e di avere = ~ invertito in ~ =

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.