Jak porównać ciągi w Bash


Odpowiedzi:


1372

Używanie zmiennych w instrukcjach if

if [ "$x" = "valid" ]; then
  echo "x has the value 'valid'"
fi

Jeśli chcesz coś zrobić, gdy nie są one zgodne, wymienić =z !=. Możesz przeczytać więcej o operacjach na łańcuchach i operacjach arytmetycznych w odpowiedniej dokumentacji.

Dlaczego używamy cytatów $x?

Chcesz znaleźć cudzysłowy $x, ponieważ jeśli jest pusty, skrypt Bash napotka błąd składniowy, jak pokazano poniżej:

if [ = "valid" ]; then

Niestandardowe użycie ==operatora

Zauważ, że Bash pozwala ==na równe traktowanie [, ale nie jest to standard .

Użyj pierwszego przypadku, w którym cudzysłowy $xsą opcjonalne:

if [[ "$x" == "valid" ]]; then

lub użyj drugiego przypadku:

if [ "$x" = "valid" ]; then

12
Jak to się ma do zaakceptowanej odpowiedzi podanej w Nieoczekiwanym błędzie operatora ? Mam ten sam błąd podczas używania [ "$1" == "on" ]. Zmiana na [„$ 1” = „on”] rozwiązała problem.
Piotr Dobrogost

78
Miejsca są potrzebne.
TAAPSogeking

6
@JohnFeminella Pisząc w skrypcie bash, powinien mieć jeden, =a nie dwa.
user13107

72
warto zauważyć, że nie możesz używać [ $x -eq "valid" ]. -eqjest operatorem porównania liczb całkowitych, a nie ciągów.
craq

2
@Alex, W jakich przypadkach (jeśli w ogóle) muszę użyć wzorca ["x$yes" == "xyes"], który poprzedza zarówno zmienną, jak i literał ciągiem znakiem x? Czy to relikt dawnych czasów, czy jest naprawdę potrzebny w niektórych sytuacjach?
lanoxx

142

Lub, jeśli nie potrzebujesz innej klauzuli:

[ "$x" == "valid" ] && echo "x has the value 'valid'"

71
A jeśli potrzebujesz klauzuli else i chcesz stworzyć zwariowaną jednowierszową: ["$ x" == "valid"] && echo "valid" || echo „nieważne”
Matt White

11
@MattWhite: jest to zwykle zły pomysł, ponieważ echomoże się nie powieść.
gniourf_gniourf

1
nawet te, które mogą się pojawić, piękne i eleganckie sposoby zarówno od marko && MattWhite
Deko

3
@gniourf_gniourf, nie ma problemu, użyj [ "$X" == "valid" ] || ( echo invalid && false ) && echo "valid" .
12431234123412341234123

4
@ 12431234123412341234123 { echo invalid && false; }jest bardziej wydajny ( echo invalid && false ), ponieważ unika płacenia za niepotrzebną podpowłokę.
Charles Duffy

84
a="abc"
b="def"

# Equality Comparison
if [ "$a" == "$b" ]; then
    echo "Strings match"
else
    echo "Strings don't match"
fi

# Lexicographic (greater than, less than) comparison.
if [ "$a" \< "$b" ]; then
    echo "$a is lexicographically smaller then $b"
elif [ "$a" \> "$b" ]; then
    echo "$b is lexicographically smaller than $a"
else
    echo "Strings are equal"
fi

Uwagi:

  1. Przestrzenie pomiędzy ifa [i ]są ważne
  2. >i <są operatorami przekierowania, więc unikaj go za pomocą \>i \<odpowiednio dla ciągów.

6
Dzięki za porównanie kolejności alfabetycznej ciągów
shadi

Mój problem polegał na tym, że $afaktycznie " "otaczał go jako część wartości literału łańcucha, dlatego musiałem użyć znaku zmiany znaczenia, $baby porównać wartości. Znalazłem to po uruchomieniu bash -x ./script.sh, flaga -x pozwala zobaczyć wartość każdego wykonania i pomaga w debugowaniu.
ShahNewazKhan

Zauważ, że porównanie kolejności alfabetycznej nie jest zgodne ze standardem POSIX, więc nie gwarantuje się, że będzie działać na platformach innych niż GNU / powłokach innych niż bash. Gwarantuje się, że tylko operacje na pubs.opengroup.org/onlinepubs/9699919799/utilities/test.html są przenośne.
Charles Duffy

62

Aby porównać ciągi znaków z symbolami wieloznacznymi, użyj

if [[ "$stringA" == *$stringB* ]]; then
  # Do something here
else
  # Do Something here
fi

12
Ważne jest, aby symboli wieloznacznych można używać tylko po prawej stronie! Zwróć też uwagę na brakujące "symbole wieloznaczne. (btw: +1 za symbole wieloznaczne!)
Scz

6
Rozszerzenie $stringB musi być notowane (i, nawiasem mówiąc, lewa strona nie musi być cytowany) if [[ $stringA = *"$stringB"* ]]; then.
gniourf_gniourf

Próbuję użyć tej samej logiki wieloznacznej dla nazwy pliku w ścieżce pliku. Ale to nie działa dla mnie. wypróbowałem wszystkie podane tutaj ciągi znaków zastępczych. ale zawsze tak jest. stringA w moim przypadku jest ścieżką do pliku / tmp / file, a stringB to „file”.
deszcz

35

Nie mogę zgodzić się z jednym z komentarzy w jednym punkcie:

[ "$x" == "valid" ] && echo "valid" || echo "invalid"

Nie, to nie jest szalony oneliner

Wygląda to tak, jakby ktoś, niewtajemniczony ...

W pewnym sensie używa wspólnych wzorców jako języka;

A kiedy nauczysz się języka.

Właściwie miło to czytać

Jest to proste wyrażenie logiczne, z jedną specjalną częścią: leniwą oceną operatorów logicznych.

[ "$x" == "valid" ] && echo "valid" || echo "invalid"

Każda część jest logicznym wyrażeniem; pierwsze mogą być prawdziwe lub fałszywe, pozostałe dwa są zawsze prawdziwe.

(
[ "$x" == "valid" ] 
&&
echo "valid"
)
||
echo "invalid"

Teraz, gdy jest oceniany, sprawdzany jest pierwszy. Jeśli jest to fałsz, to drugi argument logiki i && po nim nie ma znaczenia. Pierwsze nie jest prawdą, więc i tak nie może być pierwsze, a drugie być prawdą.
W tym przypadku jest to pierwsza strona logiki lub || fałsz, ale może być prawdą, jeśli druga strona - trzecia część - jest prawdziwa.

Tak więc trzecia część zostanie poddana ocenie - głównie napisanie wiadomości jako efekt uboczny. (Ma wynik 0dla prawdy, którego nie używamy tutaj)

Pozostałe przypadki są podobne, ale prostsze - i - obiecuję! są - mogą być - łatwe do odczytania!
(Nie mam, ale myślę, że bycie weteranem UNIX z siwą brodą bardzo pomaga w tym.)


17
... && ... || ...Zwykle boczyć na (przepraszam greybeard Unix weteranem, masz racji przez cały ten czas), a to nie jest semantycznie równoważne if ... then ... else .... Nie martw się, to częsta pułapka .
gniourf_gniourf

6
@gniourf_gniourf OP nie jest zły - nie są też prawdopodobnie ignorantami, jak sugerujesz. ... && ... || ...jest idealnie poprawnym wzorcem i wspólnym idiomem bash. Użycie tego wymaga wcześniejszej wiedzy (co warto pamiętać, jeśli na widowni są początkujący), ale OP ma włosy, aby udowodnić, że wiedzą, jak unikać otwartych pokryw włazów.
ebpa

3
@ebpa Co się stanie, jeśli instrukcja następująca po && zwróci wartość false, wykonanie zostanie wykonane po instrukcji następującej || ? Jeśli tak, to źle i być może właśnie to sugeruje gniourf
TSG

4
Myślałem, że echo to tylko przykład. Instrukcja po && może nadal zwracać niezerową wartość
TSG

2
@gniourf_gniourf +1 za opublikowanie linku do Bash Pitfalls! Bardzo przydatne!
Jaguar

21

możesz również użyć przypadku użycia / esac

case "$string" in
 "$pattern" ) echo "found";;
esac

Czy to jest równoważne czy zawiera?
ytpillai

@ytpillai, jest to równoważność. Pamiętaj, że możesz mieć wzory oddzielone |przed, przed ). inStwierdzenie jest równoznaczne thenw ifsprawozdaniu. Można argumentować, że działa na liście wzorców, gdzie każda lista ma własną deklarację co zrobić, jeśli pochodzisz z Pythona. Nie tak substring in string, ale raczej for item in list. *Jeśli chcesz elsespełnić warunek, użyj a jako ostatniego stwierdzenia . Powraca przy pierwszym spotkaniu.
mazunki

18

Poniższy skrypt czyta wiersz po wierszu z pliku o nazwie „testonthis”, a następnie porównuje każdy wiersz z prostym ciągiem, ciągiem znaków specjalnych i wyrażeniem regularnym. Jeśli się nie zgadza, skrypt wypisze wiersz, w przeciwnym razie nie.

Przestrzeń w Bash jest bardzo ważna. Więc będą działać:

[ "$LINE" != "table_name" ] 

Ale następujące nie będą:

["$LINE" != "table_name"] 

Więc proszę używać jak jest:

cat testonthis | while read LINE
do
if [ "$LINE" != "table_name" ] && [ "$LINE" != "--------------------------------" ] && [[ "$LINE" =~ [^[:space:]] ]] && [[ "$LINE" != SQL* ]]; then
echo $LINE
fi
done

Użyj tego podejścia, aby przejść przez plik. To znaczy, usuń UUoC między innymi.
fedorqui „SO przestań krzywdzić”

To nie jest ważne z tego powodu, bashale ponieważ [jest tak naprawdę zewnętrznym plikiem binarnym (ponieważ which [daje coś podobnego /usr/bin/[)
Patrick Bergner,

11

Prawdopodobnie użyłbym dopasowań wyrażenia regularnego, jeśli dane wejściowe zawierają tylko kilka poprawnych wpisów. Np. Tylko „start” i „stop” są prawidłowymi działaniami.

if [[ "${ACTION,,}" =~ ^(start|stop)$ ]]; then
  echo "valid action"
fi

Zauważ, że zmienię małe litery $ACTION, używając podwójnego przecinka. Zauważ też, że to nie zadziała w przypadku zbyt starych wersji bash.


9

Przykłady Bash 4+. Uwaga: niestosowanie cudzysłowów spowoduje problemy, gdy słowa zawierają spacje itp. Zawsze cytuj w Bash, IMO.

Oto kilka przykładów w Bash 4+:

Przykład 1, sprawdź ciąg „tak” (bez rozróżniania wielkości liter):

    if [[ "${str,,}" == *"yes"* ]] ;then

Przykład 2, sprawdź ciąg „tak” (bez rozróżniania wielkości liter):

    if [[ "$(echo "$str" | tr '[:upper:]' '[:lower:]')" == *"yes"* ]] ;then

Przykład 3, sprawdź ciąg „tak” (rozróżniana jest wielkość liter):

     if [[ "${str}" == *"yes"* ]] ;then

Przykład 4, sprawdź ciąg „tak” (rozróżniana jest wielkość liter):

     if [[ "${str}" =~ "yes" ]] ;then

Przykład 5, dopasowanie ścisłe (wielkość liter ma znaczenie):

     if [[ "${str}" == "yes" ]] ;then

Przykład 6, dopasowanie ścisłe (bez rozróżniania wielkości liter):

     if [[ "${str,,}" == "yes" ]] ;then

Przykład 7, dopasowanie ścisłe:

     if [ "$a" = "$b" ] ;then

Cieszyć się.


dla mnie (Mac GNU bash wersja 4.4.12 (1) -release x86_64-apple-darwin17.0.0), muszę użyć if [ "$a"="$b" ]lub to nie działa ... nie mogę mieć spacji wokół równych
spektrum

1

Zrobiłem to w sposób zgodny z Bash i Dash (sh):

testOutput="my test"
pattern="my"

case $testOutput in (*"$pattern"*)
    echo "if there is a match"
    exit 1
    ;;
(*)
   ! echo there is no coincidence!
;;esac

jaka jest różnica między używaniem poprzedzającego (a nieużywaniem go?
mazunki
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.