Come posso abbinare una stringa con una regex in Bash?


166

Sto cercando di scrivere uno script bash che contiene una funzione in modo che quando dato un .tar, .tar.bz2, .tar.gzdel file, ecc utilizza tar con i relativi interruttori per decomprimere il file.

Sto usando if elif quindi istruzioni che testano il nome del file per vedere con cosa finisce e non riesco a farlo corrispondere usando i metacaratteri regex.

Per salvare costantemente la riscrittura dello script che sto usando 'test' dalla riga di comando, ho pensato che la seguente istruzione dovrebbe funzionare, ho provato ogni combinazione di parentesi, virgolette e metacaratteri possibili e comunque fallisce.

test sed-4.2.2.tar.bz2 = tar\.bz2$; echo $?
(this returns 1, false)

Sono sicuro che il problema è semplice e ho cercato dappertutto, ma non riesco a capire come farlo. Qualcuno sa come posso farlo?

Risposte:


268

Per abbinare le regex devi usare l' =~operatore.

Prova questo:

[[ sed-4.2.2.tar.bz2 =~ tar.bz2$ ]] && echo matched

In alternativa, è possibile utilizzare i caratteri jolly (anziché regex) con l' ==operatore:

[[ sed-4.2.2.tar.bz2 == *tar.bz2 ]] && echo matched

Se la portabilità non è un problema, ti consiglio di utilizzare [[invece di [o testpoiché è più sicuro e più potente. Vedi Qual è la differenza tra test, [e [[? per dettagli.


7
Fai attenzione con la corrispondenza dei caratteri jolly glob nel secondo esempio. All'interno di [[]], il * non viene espanso come di solito, per abbinare i nomi dei file nella directory corrente che corrispondono a un modello. Il tuo esempio funziona, ma è davvero facile sovra-generalizzare e credere erroneamente che * significhi abbinare qualcosa in qualsiasi contesto. Funziona solo così dentro [[]]. Altrimenti, si espande ai nomi di file esistenti.
Alan Porter,

7
Ho provato a usare le virgolette sul regex e non ci sono riuscito; questa risposta ha aiutato a fare questo lavoro, check="^a.*c$";if [[ "abc" =~ $check ]];then echo match;fiabbiamo bisogno di conservare la regex su un var
Aquarius Power,

Da notare anche che regexp (come in perl) NON deve essere tra parentesi: [[ sed-4.2.2.tar.bz2 == "*tar.bz2" ]]non funzionerebbe.
pevik,

18
FWIW, la sintassi per la negazione (cioè non corrisponde ) è [[ ! foo =~ bar ]].
Skippy le Grand Gourou,

1
dash non supporta il -n 1parametro, né lo inserisce automaticamente in una $REPLYvariabile. Attento!

54

Una funzione per fare questo

extract () {
  if [ -f $1 ] ; then
      case $1 in
          *.tar.bz2)   tar xvjf $1    ;;
          *.tar.gz)    tar xvzf $1    ;;
          *.bz2)       bunzip2 $1     ;;
          *.rar)       rar x $1       ;;
          *.gz)        gunzip $1      ;;
          *.tar)       tar xvf $1     ;;
          *.tbz2)      tar xvjf $1    ;;
          *.tgz)       tar xvzf $1    ;;
          *.zip)       unzip $1       ;;
          *.Z)         uncompress $1  ;;
          *.7z)        7z x $1        ;;
          *)           echo "don't know '$1'..." ;;
      esac
  else
      echo "'$1' is not a valid file!"
  fi
}

Altra nota

In risposta a Aquarius Power nel commento sopra, We need to store the regex on a var

La variabile BASH_REMATCH viene impostata dopo aver abbinato l'espressione e $ {BASH_REMATCH [n]} corrisponderà all'ennesimo gruppo racchiuso tra parentesi, vale a dire nel seguente ${BASH_REMATCH[1]} = "compressed"e${BASH_REMATCH[2]} = ".gz"

if [[ "compressed.gz" =~ ^(.*)(\.[a-z]{1,5})$ ]]; 
then 
  echo ${BASH_REMATCH[2]} ; 
else 
  echo "Not proper format"; 
fi

(La regex sopra non è pensata per essere valida per la denominazione dei file e le estensioni, ma funziona per l'esempio)


nota anche che con BSD tar puoi usare "tar xf" per tutti i formati e non hai bisogno di comandi separati o di questa funzione.
Brava persona,

asu tar GNU o psu tar BSD per dire esplicitamente che inferisce automaticamente il tipo di compressione dall'estensione. GNU tar non lo farà automaticamente altrimenti, e suppongo dal commento di @GoodPerson che BSD tar lo faccia per impostazione predefinita.
Mark K Cowan,

7z può decomprimere .. AR, ARJ, CAB, CHM, CPIO, CramFS, DMG, EXT, FAT, GPT, HFS, IHEX, ISO, LZH, LZMA, MBR, MSI, NSIS, NTFS, QCOW2, RAR, RPM, SquashFS , UDF, UEFI, VDI, VHD, VMDK, WIM, XAR e Z. vedi 7-zip.org
mosh

14

Non ho abbastanza rappresentante per commentare qui, quindi sto inviando una nuova risposta per migliorare la risposta di dogbane. Il punto . nella regexp

[[ sed-4.2.2.tar.bz2 =~ tar.bz2$ ]] && echo matched

corrisponderà effettivamente a qualsiasi carattere, non solo al punto letterale tra 'tar.bz2', per esempio

[[ sed-4.2.2.tar4bz2 =~ tar.bz2$ ]] && echo matched
[[ sed-4.2.2.tar§bz2 =~ tar.bz2$ ]] && echo matched

o qualsiasi cosa che non richieda la fuga con '\'. La sintassi rigorosa dovrebbe quindi essere

[[ sed-4.2.2.tar.bz2 =~ tar\.bz2$ ]] && echo matched

oppure puoi andare ancora più rigoroso e includere anche il punto precedente nella regex:

[[ sed-4.2.2.tar.bz2 =~ \.tar\.bz2$ ]] && echo matched

9

Dato che stai usando bash, non è necessario creare un processo figlio per farlo. Ecco una soluzione che la esegue interamente all'interno di bash:

[[ $TEST =~ ^(.*):\ +(.*)$ ]] && TEST=${BASH_REMATCH[1]}:${BASH_REMATCH[2]}

Spiegazione: I gruppi prima e dopo la sequenza "due punti e uno o più spazi" sono memorizzati dall'operatore di corrispondenza dei pattern nell'array BASH_REMATCH.


1
Si noti che l'indice 0 contiene la corrispondenza completa e l'indice 1 e 2 contengono le corrispondenze di gruppo.
Rainer Schwarze,

3
if [[ $STR == *pattern* ]]
then
    echo "It is the string!"
else
    echo "It's not him!"
fi

Per me va bene! GNU bash, version 4.3.11(1)-release (x86_64-pc-linux-gnu)


1
Questo è estremamente pericoloso; si comporta solo senza un comportamento indefinito perché non ci sono file nella directory corrente denominata "pattern" di sottostringa letterale. Vai avanti, crea alcuni file chiamati così e l'espansione della sottostringa corrisponderà ai file e spezzerà tutto in modo orribile con heisenbugs multicolori.
i336_

Ma ho fatto un esperimento: con i file `1pattern, pattern pattern2 e pattern nella directory corrente. Questo script funziona come previsto. Potresti fornirmi il risultato del tuo test? @ i336_
juan cortez

2
@ i336: non la penso così. All'interno [[ ... ]], il modello rhs glob non si espande secondo la directory corrente, come farebbe normalmente.
user1934428

@ i336_ No. All'interno [[...]], Bash non esegue l'espansione del nome file. Nel manuale di bash,Word splitting and filename expansion are not performed on the words between the [[ and ]];
jinbeom hong

@jinbeomhong: TIL. Buono a sapersi, grazie!
i336_

2

shopas -s nocasematch

if [[ sed-4.2.2.$LINE =~ (yes|y)$ ]]
 then exit 0 
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.