Wie kann ich eine Zeichenfolge mit einem regulären Ausdruck in Bash abgleichen?

166

Ich versuche, ein Bash-Skript zu schreiben, das eine Funktion enthält. Wenn also eine Datei usw. angegeben .tarwird .tar.bz2, .tar.gzwird tar mit den entsprechenden Schaltern verwendet, um die Datei zu dekomprimieren.

Ich verwende if elif then-Anweisungen, die den Dateinamen testen, um zu sehen, womit er endet, und ich kann ihn nicht mithilfe von Regex-Metazeichen in Übereinstimmung bringen.

Um zu vermeiden, dass das Skript, das ich mit 'test' in der Befehlszeile verwende, ständig neu geschrieben wird, dachte ich, dass die folgende Anweisung funktionieren sollte. Ich habe jede mögliche Kombination von Klammern, Anführungszeichen und Metazeichen ausprobiert und es schlägt immer noch fehl.

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

Ich bin mir sicher, dass das Problem einfach ist und ich habe überall gesucht, aber ich kann mir nicht vorstellen, wie es geht. Weiß jemand, wie ich das machen kann?

user1587462
quelle

Antworten:

268

Um reguläre Ausdrücke abzugleichen, müssen Sie den =~Operator verwenden.

Versuche dies:

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

Alternativ können Sie mit dem ==Operator Platzhalter (anstelle von regulären Ausdrücken) verwenden :

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

Wenn die Portabilität kein Problem darstellt, empfehle ich die Verwendung [[anstelle von [oder, testda sie sicherer und leistungsfähiger ist. Siehe Was ist der Unterschied zwischen Test [und [[? für Details.

Dogbane
quelle
7
Seien Sie vorsichtig mit dem Glob-Platzhalterabgleich im zweiten Beispiel. In [[]] wird das * nicht wie gewohnt erweitert, um Dateinamen im aktuellen Verzeichnis abzugleichen, die einem Muster entsprechen. Ihr Beispiel funktioniert, aber es ist wirklich leicht zu verallgemeinern und fälschlicherweise zu glauben, dass * bedeutet, etwas in zu finden jeder Kontext. Das funktioniert nur in [[]]. Andernfalls wird es auf die vorhandenen Dateinamen erweitert.
Alan Porter
7
Ich habe versucht, Anführungszeichen für den regulären Ausdruck zu verwenden, und bin fehlgeschlagen. Diese Antwort hat dazu beigetragen, dass diese Arbeit funktioniert. check="^a.*c$";if [[ "abc" =~ $check ]];then echo match;fiWir müssen den regulären Ausdruck auf einem Var speichern
Aquarius Power
Beachten Sie auch, dass Regexp (wie in Perl) NICHT in Klammern stehen darf: [[ sed-4.2.2.tar.bz2 == "*tar.bz2" ]]würde nicht funktionieren.
Pevik
18
FWIW ist die Syntax für die Negation (dh stimmt nicht überein ) [[ ! foo =~ bar ]].
Skippy le Grand Gourou
1
dash unterstützt den -n 1Parameter nicht und fügt ihn auch nicht automatisch in eine $REPLYVariable ein. Achtung!
54

Eine Funktion, um dies zu tun

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
}

Anderer Hinweis

Als Antwort auf Aquarius Power im obigen Kommentar We need to store the regex on a var

Die Variable BASH_REMATCH wird gesetzt, nachdem Sie mit dem Ausdruck übereinstimmen, und $ {BASH_REMATCH [n]} stimmt mit der in Klammern eingeschlossenen n-ten Gruppe überein, dh in den folgenden ${BASH_REMATCH[1]} = "compressed"und${BASH_REMATCH[2]} = ".gz"

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

(Der obige reguläre Ausdruck ist nicht für die Benennung und Erweiterung von Dateien gültig, funktioniert jedoch für das Beispiel.)

Dualität
quelle
Beachten Sie auch, dass Sie mit BSD tar "tar xf" für alle Formate verwenden können und keine separaten Befehle oder diese Funktion benötigen.
Gute Person
aauf GNU tar oder pauf BSD tar, um explizit anzuweisen, den Komprimierungstyp automatisch aus der Erweiterung abzuleiten. GNU tar wird es sonst nicht automatisch tun, und ich vermute aus @ GoodPersons Kommentar, dass BSD tar dies standardmäßig tut.
Mark K Cowan
7z kann auspacken. 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 und Z. siehe 7-zip.org
mosh
14

Ich habe nicht genug Repräsentanten, um hier einen Kommentar abzugeben, daher reiche ich eine neue Antwort ein, um die Antwort von dogbane zu verbessern. Der Punkt . im regulären Ausdruck

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

stimmt tatsächlich mit jedem Zeichen überein, nicht nur mit dem Literalpunkt zwischen 'tar.bz2'

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

oder alles, was es nicht erfordert, mit '\' zu entkommen. Die strenge Syntax sollte dann sein

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

oder Sie können noch strenger vorgehen und auch den vorherigen Punkt in den regulären Ausdruck aufnehmen:

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

Da Sie bash verwenden, müssen Sie dafür keinen untergeordneten Prozess erstellen. Hier ist eine Lösung, die es vollständig innerhalb von Bash ausführt:

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

Erläuterung: Die Gruppen vor und nach der Sequenz "Doppelpunkt und ein oder mehrere Leerzeichen" werden vom Mustervergleichsoperator im Array BASH_REMATCH gespeichert.

user1934428
quelle
1
Beachten Sie, dass Index 0 die vollständige Übereinstimmung enthält und Index 1 und 2 die Gruppenübereinstimmungen enthalten.
Rainer Schwarze
3
if [[ $STR == *pattern* ]]
then
    echo "It is the string!"
else
    echo "It's not him!"
fi

Funktioniert bei mir! GNU bash, version 4.3.11(1)-release (x86_64-pc-linux-gnu)

Juan Cortez
quelle
1
Das ist extrem gefährlich; Es verhält sich für Sie nur ohne undefiniertes Verhalten, da sich im aktuellen Verzeichnis keine Dateien mit dem Namen "Muster" des Literal-Teilstrings befinden. Erstellen Sie einige Dateien mit diesem Namen, und die Teilzeichenfolgenerweiterung stimmt mit den Dateien überein und zerstört alles schrecklich mit mehrfarbigen Heisenbugs.
i336_
Aber ich habe ein Experiment durchgeführt: mit den Dateien `1pattern, pattern pattern2 und pattern im aktuellen Verzeichnis. Dieses Skript funktioniert wie erwartet. Könnten Sie mir bitte Ihr Testergebnis mitteilen? @ i336_
Cortez
2
@ i336: Das glaube ich nicht. Innerhalb [[ ... ]], ist die rechte Seite glob Muster nicht erweitern das aktuelle Verzeichnis nach tho, wie es normalerweise tun würde.
user1934428
@ i336_ Nein. Innerhalb von [[...]]führt Bash keine Dateinamenerweiterung durch. Im Bash-HandbuchWord splitting and filename expansion are not performed on the words between the [[ and ]];
Jinbeom Hong
@ Jinbeomhong: Bis. Das ist gut zu wissen, danke!
i336_
2

shopt -s nocasematch

if [[ sed-4.2.2.$LINE =~ (yes|y)$ ]]
 then exit 0 
fi
Shyam Gupta
quelle