Jak mogę dopasować ciąg do wyrażenia regularnego w Bash?


166

Próbuję napisać skrypt bash, który zawiera funkcję, więc kiedy dali .tar, .tar.bz2,.tar.gz itp wykorzystuje plik tar z odpowiednimi przełącznikami do dekompresji pliku.

Używam instrukcji if elif then, które testują nazwę pliku, aby zobaczyć, na czym się kończy, i nie mogę dopasować jej przy użyciu metaznaków wyrażenia regularnego.

Aby zaoszczędzić na ciągłym przepisywaniu skryptu, którego używam „test” w wierszu poleceń, pomyślałem, że poniższe stwierdzenie powinno działać, wypróbowałem każdą możliwą kombinację nawiasów, cudzysłowów i metacharatów i nadal się nie udaje.

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

Jestem pewien, że problem jest prosty i szukałem wszędzie, ale nie mogę pojąć, jak to zrobić. Czy ktoś wie, jak mogę to zrobić?

Odpowiedzi:


268

Aby dopasować wyrażenia regularne, musisz użyć rozszerzenia =~ operatora.

Spróbuj tego:

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

Alternatywnie możesz użyć symboli wieloznacznych (zamiast wyrażeń regularnych) z ==operatorem:

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

Jeśli przenośność nie jest problemem, polecam używanie [[zamiast [lub, testponieważ jest bezpieczniejsze i mocniejsze. Zobacz Jaka jest różnica między test, [i [[? dla szczegółów.


7
Uważaj na dopasowywanie symboli wieloznacznych glob w drugim przykładzie. Wewnątrz [[]] znak * nie jest rozwijany, jak to zwykle bywa, aby dopasować nazwy plików w bieżącym katalogu, które pasują do wzorca. Twój przykład działa, ale naprawdę łatwo jest go nadmiernie uogólnić i błędnie uwierzyć, że * oznacza dopasowanie czegokolwiek w dowolny kontekst. Działa tak tylko w [[]]. W przeciwnym razie rozszerza się do istniejących nazw plików.
Alan Porter

7
Próbowałem użyć cudzysłowu w wyrażeniu regularnym i nie udało mi się; ta odpowiedź pomogła w wykonaniu tej pracy check="^a.*c$";if [[ "abc" =~ $check ]];then echo match;fi, musimy przechowywać wyrażenie regularne w zmiennej
Aquarius Power

Należy również zauważyć, że regexp (jak w perlu) NIE może być w nawiasach: [[ sed-4.2.2.tar.bz2 == "*tar.bz2" ]]nie zadziała.
pevik

18
FWIW, składnia negacji (tj. Nie pasuje ) to [[ ! foo =~ bar ]].
Skippy le Grand Gourou

1
myślnik nie obsługuje -n 1parametru, ani też nie umieszcza go automatycznie w $REPLYzmiennej. Uważaj!

54

Funkcja do tego

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
}

Inna uwaga

W odpowiedzi na Moc Wodnika w powyższym komentarzu, We need to store the regex on a var

Zmienna BASH_REMATCH jest ustawiana po dopasowaniu wyrażenia, a $ {BASH_REMATCH [n]} dopasuje n-tą grupę zawiniętą w nawiasy, tj. W następujących ${BASH_REMATCH[1]} = "compressed"i${BASH_REMATCH[2]} = ".gz"

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

(Powyższe wyrażenie regularne nie ma być poprawne dla nazewnictwa plików i rozszerzeń, ale działa w tym przykładzie)


Zauważ również, że z tar BSD możesz używać "tar xf" dla wszystkich formatów i nie potrzebujesz osobnych poleceń ani żadnej funkcji.
Good Person

ana tarach GNU lub pna tarach BSD, aby jawnie powiedzieć mu, aby automatycznie wywnioskował typ kompresji z rozszerzenia. W przeciwnym razie GNU tar nie zrobi tego automatycznie i zgaduję na podstawie komentarza @GoodPerson, że tar BSD robi to domyślnie.
Mark K Cowan

7z może rozpakować. 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 i Z. patrz 7-zip.org
mosh

14

Nie mam wystarczającej liczby przedstawicieli, aby komentować tutaj, więc przesyłam nową odpowiedź, aby poprawić odpowiedź dogbane. Kropka . w wyrażeniu regularnym

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

faktycznie dopasuje dowolny znak, nie tylko na przykład dosłowną kropkę między „tar.bz2”

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

lub cokolwiek, co nie wymaga ucieczki z '\'. W takim przypadku powinna być ścisła składnia

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

lub możesz pójść jeszcze bardziej restrykcyjnie i uwzględnić również poprzednią kropkę w wyrażeniu regularnym:

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

9

Ponieważ używasz basha, nie musisz w tym celu tworzyć procesu potomnego. Oto jedno rozwiązanie, które wykonuje to całkowicie w bash:

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

Objaśnienie: Grupy przed i po sekwencji „dwukropek i co najmniej jedna spacja” są zapisywane przez operator dopasowania do wzorca w tablicy BASH_REMATCH.


1
Zauważ, że indeks 0 zawiera pełne dopasowanie, a indeks 1 i 2 zawiera dopasowania grup.
Rainer Schwarze

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

Pracuje dla mnie! GNU bash, version 4.3.11(1)-release (x86_64-pc-linux-gnu)


1
To jest niezwykle niebezpieczne; zachowuje się on tylko bez niezdefiniowanego zachowania, ponieważ w bieżącym katalogu nie ma plików o nazwie „wzorzec” literału. Śmiało, utwórz kilka plików o takich nazwach, a rozszerzenie podciągów dopasuje pliki i wszystko okropnie zepsuje wielobarwnymi heisenbugami.
i336_

Ale zrobiłem eksperyment: z plikami `1pattern, pattern pattern2 i pattern w bieżącym katalogu. Ten skrypt działa zgodnie z oczekiwaniami. Czy mógłbyś podać mi swój wynik testu? @ i336_
juan cortez

2
@ i336: Nie sądzę. Wewnątrz [[ ... ]]wzorzec rhs glob nie rozwija się zgodnie z bieżącym katalogiem, jak to zwykle bywa.
user1934428

@ i336_ Nie. W ramach [[...]]Bash nie wykonuje rozszerzania nazw plików. W podręczniku bash,Word splitting and filename expansion are not performed on the words between the [[ and ]];
jinbeom hong

@jinbeomhong: TIL. Dobrze wiedzieć, dzięki!
i336_

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.