usa l'espressione regolare nella condizione if in bash


88

Mi chiedo la regola generale per usare l'espressione regolare nella clausola if in bash?

Ecco un esempio

$ gg=svm-grid-ch  
$ if [[ $gg == *grid* ]] ; then echo $gg; fi  
svm-grid-ch  
$ if [[ $gg == ^....grid* ]] ; then echo $gg; fi  
$ if [[ $gg == ....grid* ]] ; then echo $gg; fi  
$ if [[ $gg == s...grid* ]] ; then echo $gg; fi  
$   

Perché gli ultimi tre non corrispondono?

Spero che tu possa dare quante più regole generali possibili, non solo per questo esempio.

Risposte:


129

Quando si utilizza un modello glob, un punto interrogativo rappresenta un singolo carattere e un asterisco rappresenta una sequenza di zero o più caratteri:

if [[ $gg == ????grid* ]] ; then echo $gg; fi

Quando si utilizza un'espressione regolare, un punto rappresenta un singolo carattere e un asterisco rappresenta zero o più del carattere precedente. Quindi " .*" rappresenta zero o più caratteri, " a*" rappresenta zero o più "a", " [0-9]*" rappresenta zero o più cifre. Un altro utile (tra i tanti) è il segno più che rappresenta uno o più dei caratteri precedenti. Quindi " [a-z]+" rappresenta uno o più caratteri alfabetici minuscoli (nella lingua C - e alcuni altri).

if [[ $gg =~ ^....grid.*$ ]] ; then echo $gg; fi

Quindi ci sono due modi per la corrispondenza delle stringhe: glob pattern ed espressione regolare? Glob pettern non viene utilizzato solo per i nomi dei file? In bash, quando usare il modello glob e quando usare l'espressione regolare? Grazie!
Tim

1
@Tim: Globbing è disponibile nella maggior parte o in tutte le versioni di Bash. La corrispondenza delle espressioni regolari è disponibile solo nella versione 3 e successive, ma ti consiglio di usarla solo nella 3.2 e successive. Le espressioni regolari sono molto più versatili del globbing.
Dennis Williamson,


14
if [[ $gg =~ ^....grid.* ]]

1
Dovresti essere in grado di usare ". {4}" invece di "....", cioè "^. {4} grid. *". Può essere più facile da leggere e da capire.
user276648

8

L'aggiunta di questa soluzione con grepe shincorporati di base per coloro che sono interessati a una soluzione più portabile (indipendentemente dalla bashversione; funziona anche con semplici vecchi sh, su piattaforme non Linux ecc.)

# GLOB matching
gg=svm-grid-ch    
case "$gg" in
   *grid*) echo $gg ;;
esac

# REGEXP    
if echo "$gg" | grep '^....grid*' >/dev/null ; then echo $gg ; fi    
if echo "$gg" | grep '....grid*' >/dev/null ; then echo $gg ; fi    
if echo "$gg" | grep 's...grid*' >/dev/null ; then echo $gg ; fi    

# Extended REGEXP
if echo "$gg" | egrep '(^....grid*|....grid*|s...grid*)' >/dev/null ; then
  echo $gg
fi    

Alcune grepincarnazioni supportano anche l' -qopzione (silenziosa) come alternativa al reindirizzamento a /dev/null, ma il reindirizzamento è di nuovo il più portabile.


dimenticato una chiusura ")" per egrep
ghostdog74

5
Usa grep -qinvece di grep >/dev/null.
bfontaine

3

@OPERAZIONE,

Glob pettern non viene utilizzato solo per i nomi dei file?

No, il modello "glob" non viene utilizzato solo per i nomi dei file. puoi usarlo anche per confrontare le stringhe. Nei tuoi esempi, puoi usare case / esac per cercare schemi di stringhe.

 gg=svm-grid-ch 
 # looking for the word "grid" in the string $gg
 case "$gg" in
    *grid* ) echo "found";;
 esac

 # [[ $gg =~ ^....grid* ]]
 case "$gg" in ????grid*) echo "found";; esac 

 # [[ $gg =~ s...grid* ]]
 case "$gg" in s???grid*) echo "found";; esac

In bash, quando usare il modello glob e quando usare l'espressione regolare? Grazie!

Le espressioni regolari sono più versatili e "convenienti" dei "modelli glob", tuttavia, a meno che non si eseguano compiti complessi che "globbing / globbing esteso" non possono fornire facilmente, non è necessario utilizzare regex. Le espressioni regolari non sono supportate per la versione di bash <3.2 (come menzionato da Dennis), ma puoi comunque usare il globbing esteso (impostando extglob). per il globbing esteso, vedere qui e alcuni semplici esempi qui .

Aggiornamento per OP: esempio per trovare file che iniziano con 2 caratteri (i punti "." Significa 1 carattere) seguiti da "g" utilizzando regex

es. output

$ shopt -s dotglob
$ ls -1 *
abg
degree
..g

$ for file in *; do [[ $file =~ "..g" ]] && echo $file ; done
abg
degree
..g

In quanto sopra, i file vengono abbinati perché i loro nomi contengono 2 caratteri seguiti da "g". (cioè ..g).

L'equivalente con globbing sarà qualcosa del genere: (guarda il riferimento per il significato di ?e *)

$ for file in ??g*; do echo $file; done
abg
degree
..g

Grazie ghostdog74. In Bash con versione superiore alla 3.2, è possibile utilizzare espressioni regolari per sostituire glob pattern ovunque appaia quest'ultimo? O l'espressione regolare può essere utilizzata solo in alcune circostanze speciali? Ad esempio, ho scoperto che "ls ?? g" funziona mentre "ls ..g" non lo è.
Tim

Non c'è modo di impedirti di usare regex se ce n'è bisogno. Tocca a voi. Nota, la sintassi delle espressioni regolari è diversa dalla sintassi del globbing della shell. quindi ls ..gnon funziona. Stai dicendo alla shell di cercare un file denominato ..g. Per quanto riguarda l'apprendimento sulla sintassi regex, si può provare perldoc perlretut, perldoc perlrequicko fare una info sedsulla riga di comando.
ghostdog74
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.