Jak ustalić, czy ciąg znaków jest podciągiem innego ciągu w bash?

49

Chcę sprawdzić, czy ciąg znaków znajduje się w części innego ciągu.
na przykład:

'ab' in 'abc' -> true
'ab' in 'bcd' -> false

Jak mogę to zrobić w warunkowym skrypcie bash?

Lucio
źródło

Odpowiedzi:

27

Możesz użyć formularza, w ${VAR/subs}którym VARznajduje się większy ciąg i subsjest to podciąg, który próbujesz znaleźć:

my_string=abc
substring=ab
if [ "${my_string/$substring}" = "$my_string" ] ; then
  echo "${substring} is not in ${my_string}"
else
  echo "${substring} was found in ${my_string}"
fi

Działa ${VAR/subs}to, ponieważ jest równe, $VARale z pierwszym wystąpieniem ciągu subsusuniętego, w szczególności jeśli $VARnie zawiera słowa subs, nie zostanie zmodyfikowane.

edwin
źródło
Myślę, że powinieneś zmienić kolejność echoinstrukcji. Ponieważ dostajęab is not in abc
Lucio
Masz rację! : P
edwin
Mmm .. Nie, skrypt jest zły. Tak otrzymuję ab was found in abc, ale jeśli użyję substring=z, dostajęz was found in abc
Lucio
1
Teraz rozumiem ab is not in abc. Ale z was found in abc… To zabawne: D
Lucio
1
Hه! Echa były na początku tego! XD
edwin
47

[[ "bcd" =~ "ab" ]]
[[ "abc" =~ "ab" ]]

nawiasy klamrowe są do testu, a ponieważ są to podwójne nawiasy klamrowe, może to oznaczać dodatkowe testy =~.

Więc możesz użyć tego formularza

var1="ab"
var2="bcd"
if [[ "$var2" =~ "$var1" ]]; then
    echo "pass"
else
    echo "fail"
fi

Edycja: poprawione „= ~”, odwróciło się.

poważny
źródło
1
Dostaję failz tymi parametrami:var2="abcd"
Lucio
3
@Lucio Prawidłowe jest [[ $string =~ $substring ]]. Zaktualizowałem odpowiedź.
Eric Carvalho
12

Używanie wzorców nazw plików bash (zwanych również wzorcami „glob”)

substr=ab
[[ abc == *"$substr"* ]] && echo yes || echo no    # yes
[[ bcd == *"$substr"* ]] && echo yes || echo no    # no
Glenn Jackman
źródło
if [["$ JAVA_OPTS"! = "-XX: + UseCompressedOops" ]]; następnie wyeksportuj JAVA_OPTS = "$ JAVA_OPTS -XX: + UseCompressedOops"; fi
Mike Slinn
10

Poniższe dwa podejścia będą działać w dowolnym środowisku zgodnym z POSIX, nie tylko w bash:

substr=ab
for s in abc bcd; do
    if case ${s} in *"${substr}"*) true;; *) false;; esac; then
        printf %s\\n "'${s}' contains '${substr}'"
    else
        printf %s\\n "'${s}' does not contain '${substr}'"
    fi
done
substr=ab
for s in abc bcd; do
    if printf %s\\n "${s}" | grep -qF "${substr}"; then
        printf %s\\n "'${s}' contains '${substr}'"
    else
        printf %s\\n "'${s}' does not contain '${substr}'"
    fi
done

Oba powyższe dane wyjściowe:

'abc' contains 'ab'
'bcd' does not contain 'ab'

Ta pierwsza ma tę zaletę, że nie spawnuje osobnego grepprocesu.

Zauważ, że używam printf %s\\n "${foo}"zamiast, echo "${foo}"ponieważ echomoże się mangować, ${foo}jeśli zawiera odwrotne ukośniki.

Richard Hansen
źródło
Pierwsza wersja doskonale sprawdza się w wyszukiwaniu podciągów nazwy monitora na liście xrandrnazw monitorów przechowywanych w zmiennej. +1 i witamy w klubie 1K rep :)
WinEunuuchs2Unix 10'18
6

instrukcja case shell

To najbardziej przenośne rozwiązanie, działa nawet na starych pociskach Bourne'a i Korn

#!/bin/bash
case "abcd" in
    *$1*) echo "It's a substring" ;;
    *) echo "Not a substring" ;;
esac

Przykładowy przebieg:

$ ./case_substr.sh "ab"                                                                                           
It's a substring
$ ./case_substr.sh "whatever"                                                                                     
Not a substring

Pamiętaj, że nie musisz specjalnie używać echo, możesz używać exit 1i exit 0oznaczać sukces lub porażkę.

Możemy również stworzyć funkcję (w razie potrzeby w dużych skryptach) z określonymi wartościami zwracanymi (0 w przypadku dopasowania, 1 w przypadku braku dopasowania):

$ ./substring_function.sh                                  
ab is substring

$ cat substring_function.sh                                
#!/bin/sh

is_substring(){
    case "$2" in
        *$1*) return 0;;
        *) return 1;;
    esac
}

main(){
   if is_substring "ab" "abcdefg"
   then
       echo "ab is substring"
   fi
}

main $@

grep

$ grep -q 'ab' <<< "abcd" && echo "it's a substring" || echo "not a substring"                                    
it's a substring

To szczególne podejście jest przydatne w przypadku instrukcji if-else w bash. Również w większości przenośne

AWK

$ awk '$0~/ab/{print "it is a substring"}' <<< "abcd"                                                             
it is a substring

Pyton

$ python -c 'import sys;sys.stdout.write("it is a substring") if "ab" in sys.stdin.read() else exit(1)' <<< "abcd"
it is a substring

Rubin

$ ruby -e ' puts "is substring" if  ARGV[1].include? ARGV[0]'  "ab" "abcdef"                                             
is substring
Sergiy Kolodyazhnyy
źródło
+1 za przekroczenie wszystkich. Zauważyłem tutaj i na innych stronach wymiany stosów brak odpowiedzi zwraca przesunięcie podłańcucha w ciągu. Która jest dzisiejsza misja :)
WinEunuuchs2Unix
@ WinEunuuchs2Unix Zamierzasz to zrobić w bash?
Sergiy Kolodyazhnyy
Tak i nie. Robię projekt Frankensteina, w którym Python pobiera wszystkie metadane wiadomości gmail.com, a bash analizuje je i wyświetla listę GUI z drążeniem w dół. Znalazłem odpowiedź tutaj: stackoverflow.com/questions/5031764/…
WinEunuuchs2Unix
@ WinEunuuchs2Unix OK. Brzmi interesująco. Osobiście wolałbym analizować wszystko w Pythonie. Ma znacznie więcej możliwości przetwarzania tekstu niż sam bash.
Sergiy Kolodyazhnyy
Znam twoje preferencje od około dwóch lat i szanuję je. Ale właśnie uczę się języka Python i zmuszanie mnie do pracy w nim wydaje mi się kłopotliwe. Nie wspominając już o wszystkich procesach tablicowych, z którymi już dobrze mi się gra w bash. Ale przynajmniej napisałem swój pierwszy skrypt Pythona, aby wyssać wszystko z gmail.com google'a do płaskiego pliku Linux, prawda? :)
WinEunuuchs2Unix
5

Przeszkadza [[i ":

[[ $a == z* ]]   # True if $a starts with an "z" (pattern matching).
[[ $a == "z*" ]] # True if $a is equal to z* (literal matching).

[ $a == z* ]     # File globbing and word splitting take place.
[ "$a" == "z*" ] # True if $a is equal to z* (literal matching).

Tak jak powiedział @glenn_jackman, ale pamiętaj, że jeśli owiniesz cały drugi termin podwójnymi cudzysłowami, przełączy on test na dosłowne dopasowanie.

Źródło: http://tldp.org/LDP/abs/html/comparison-ops.html

Campa
źródło
4

Podobne do odpowiedzi edwina, ale z ulepszoną przenośnością dla posix & ksh i odrobiną mniej hałasu niż Richard:

substring=ab

string=abc
if [ "$string" != "${string%$substring*}" ]; then
    echo "$substring IS in $string"
else
    echo "$substring is NOT in $string"
fi

string=bcd
if [ "$string" != "${string%$substring*}" ]; then
    echo "$string contains $substring"
else
    echo "$string does NOT contain $substring"
fi

Wynik:

abc contains ab
bcd does NOT contain ab
laubster
źródło