Jak sprawdzić, czy łańcuch zawiera podłańcuch w Bash

Mam string w Bash:

string="My string"

Jak mogę sprawdzić, czy zawiera inny ciąg znaków?

if [ $string ?? 'foo' ]; then
  echo "It's there!"
fi

Gdzie ?? jest mój nieznany operator. Czy używam echo i grep?

if echo "$string" | grep 'foo'; then
  echo "It's there!"
fi
To wygląda trochę niezdarnie.
Author: codeforester, 2008-10-23

21 answers

Możesz użyć Marcus ' s answer (*wildcards) poza instrukcją case, również, jeśli używasz podwójnych nawiasów:

string='My long string'
if [[ $string == *"My long"* ]]; then
  echo "It's there!"
fi

Zauważ, że spacje w łańcuchu igieł muszą być umieszczone pomiędzy podwójnymi cudzysłowami, a * symbole wieloznaczne powinny znajdować się na zewnątrz.

 2618
Author: Adam Bellaire,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-10-05 14:14:14

Jeśli wolisz podejście regex:

string='My string';

if [[ $string =~ .*My.* ]]
then
   echo "It's there!"
fi
 400
Author: Matt Tardiff,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-23 20:10:53

Nie jestem pewien co do użycia instrukcji if, ale można uzyskać podobny efekt za pomocą instrukcji case:

case "$string" in 
  *foo*)
    # Do stuff
    ;;
esac
 246
Author: Marcus Griep,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-23 12:47:04

Zgodna odpowiedź

Ponieważ istnieje już wiele odpowiedzi wykorzystujących funkcje specyficzne dla Basha, istnieje sposób pracy pod uboższymi powłokami, takimi jak busybox :

[ -z "${string##*$reqsubstr*}" ]

W praktyce może to dać:

string='echo "My string"'
for reqsubstr in 'o "M' 'alt' 'str';do
  if [ -z "${string##*$reqsubstr*}" ] ;then
      echo "String '$string' contain substring: '$reqsubstr'."
    else
      echo "String '$string' don't contain substring: '$reqsubstr'."
    fi
  done

To zostało przetestowane pod bash, dash, ksh i ash (busybox), a wynik jest zawsze:

String 'echo "My string"' contain substring: 'o "M'.
String 'echo "My string"' don't contain substring: 'alt'.
String 'echo "My string"' contain substring: 'str'.

W jedną funkcję

Jak zapytał @EeroAaltonen oto wersja tego samego demo, testowane pod tymi samymi muszlami:

myfunc() {
    reqsubstr="$1"
    shift
    string="$@"
    if [ -z "${string##*$reqsubstr*}" ] ;then
        echo "String '$string' contain substring: '$reqsubstr'.";
      else
        echo "String '$string' don't contain substring: '$reqsubstr'." 
    fi
}

Potem:

$ myfunc 'o "M' 'echo "My String"'
String 'echo "My String"' contain substring 'o "M'.

$ myfunc 'alt' 'echo "My String"'
String 'echo "My String"' don't contain substring 'alt'.

Notice: musisz escape lub Double enclose cudzysłów i / lub podwójne cudzysłowy:

$ myfunc 'o "M' echo "My String"
String 'echo My String' don't contain substring: 'o "M'.

$ myfunc 'o "M' echo \"My String\"
String 'echo "My String"' contain substring: 'o "M'.

Prosta funkcja

To zostało przetestowane pod busybox, dash i oczywiście bash :

stringContain() { [ -z "${2##*$1*}" ]; }
To wszystko!

Then now:

$ if stringContain 'o "M3' 'echo "My String"';then echo yes;else echo no;fi
no
$ if stringContain 'o "M' 'echo "My String"';then echo yes;else echo no;fi
yes

... Lub jeśli podany ciąg znaków może być pusty, jak wskazuje @Sjlver, funkcja zostań:

stringContain() { [ -z "${2##*$1*}" ] && [ -z "$1" -o -n "$2" ]; }

Lub zgodnie z sugestią komentarz Adriana Güntera , unikając -o switche:

stringContain() { [ -z "${2##*$1*}" ] && { [ -z "$1" ] || [ -n "$2" ] ;} ; }

Z pustymi łańcuchami:

$ if stringContain '' ''; then echo yes; else echo no; fi
yes
$ if stringContain 'o "M' ''; then echo yes; else echo no; fi
no
 147
Author: F. Hauri,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-12 06:35:38

Należy pamiętać, że skrypty powłoki są mniej językiem, a bardziej zbiorem poleceń. Instynktownie myślisz, że ten "język" wymaga podążania za if z [ lub [[. Oba są po prostu poleceniami, które zwracają status zakończenia wskazujący na sukces lub porażkę (tak jak każde inne polecenie). Z tego powodu użyłbym grep, a nie [ polecenia.

Po prostu zrób:

if grep -q foo <<<"$string"; then
    echo "It's there"
fi

Teraz, gdy myślisz o if jako o sprawdzaniu statusu wyjścia z polecenie, które następuje po nim (wraz z dwukropkiem). Dlaczego nie przemyśleć źródła łańcucha, który testujesz?

## Instead of this
filetype="$(file -b "$1")"
if grep -q "tar archive" <<<"$filetype"; then
#...

## Simply do this
if file -b "$1" | grep -q "tar archive"; then
#...

Opcja -q sprawia, że grep nie wyświetla niczego, ponieważ chcemy tylko kodu zwrotnego. <<< sprawia, że powłoka rozszerza następne słowo i używa go jako wejścia do polecenia, jednowierszowej wersji << tutaj dokumentu (nie jestem pewien, czy jest to standard, czy bashism).

 119
Author: Mark Baker,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-12 06:41:24

Przyjęta odpowiedź jest najlepsza, ale ponieważ istnieje więcej niż jeden sposób, oto inne rozwiązanie:

if [ "$string" != "${string/foo/}" ]; then
    echo "It's there!"
fi

${var/search/replace} jest {[2] } z pierwszą instancją search zastąpioną przez replace, jeśli zostanie znaleziona(nie zmienia się $var). Jeśli spróbujesz zastąpić foo przez nic, a łańcuch znaków się zmienił, to oczywiście foo został znaleziony.

 75
Author: ephemient,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-06-12 05:33:48

Więc jest wiele przydatnych rozwiązań na pytanie - ale który jest najszybszy / wykorzystuje najmniej zasobów?

Testy powtarzane przy użyciu tej ramki:

/usr/bin/time bash -c 'a=two;b=onetwothree; x=100000; while [ $x -gt 0 ]; do TEST ; x=$(($x-1)); done'

Test zastępczy za każdym razem:

[[ $b =~ $a ]]           2.92user 0.06system 0:02.99elapsed 99%CPU

[ "${b/$a//}" = "$b" ]   3.16user 0.07system 0:03.25elapsed 99%CPU

[[ $b == *$a* ]]         1.85user 0.04system 0:01.90elapsed 99%CPU

case $b in *$a):;;esac   1.80user 0.02system 0:01.83elapsed 99%CPU

doContain $a $b          4.27user 0.11system 0:04.41elapsed 99%CPU

(doContain był w odpowiedzi F. Houriego)

I dla chichotów:

echo $b|grep -q $a       12.68user 30.86system 3:42.40elapsed 19%CPU !ouch!

Tak więc prosta opcja podstawienia predykatalnie wygrywa, czy w rozszerzonym teście, czy w przypadku. Walizka jest przenośna.

Przelot do 100000 grepów jest przewidywalnie bolesny! Stara zasada o używaniu narzędzia zewnętrzne bez potrzeby.
 34
Author: Paul Hedderly,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-08-27 19:42:38

To również działa:

if printf -- '%s' "$haystack" | egrep -q -- "$needle"
then
  printf "Found needle in haystack"
fi

A test negatywny to:

if ! printf -- '%s' "$haystack" | egrep -q -- "$needle"
then
  echo "Did not find needle in haystack"
fi

Przypuszczam, że ten styl jest nieco bardziej klasyczny -- mniej zależny od funkcji powłoki Bash.

Argument -- to czysty POSIX, używany do ochrony przed ciągami wejściowymi podobnymi do opcji, takich jak --abc czy -a.

Uwaga: w ciasnej pętli kod ten będzie dużo wolniejszy niż używanie wewnętrznych funkcji powłoki Bash, ponieważ jeden (lub dwa) oddzielne procesy będą tworzone i połączone za pomocą rury.

 23
Author: kevinarpe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-11 23:53:20

A może tak:

text="   <tag>bmnmn</tag>  "
if [[ "$text" =~ "<tag>" ]]; then
   echo "matched"
else
   echo "not matched"
fi
 12
Author: bn.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-12-15 16:37:57

Ta odpowiedź przepełnienia stosu była jedyną, która zatrzymała znaki spacji i kreski:

# For null cmd arguments checking   
to_check=' -t'
space_n_dash_chars=' -'
[[ $to_check == *"$space_n_dash_chars"* ]] && echo found
 9
Author: Yordan Georgiev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:34:48

Jak wspomniał Paweł w swoim porównaniu wydajności:

if echo "abcdefg" | grep -q "bcdef"; then
    echo "String contains is true."
else
    echo "String contains is not true."
fi

Jest to zgodne ze standardem POSIX, podobnie jak' case '$string ' in 'answer dostarczone przez Marcusa, ale jest nieco łatwiejsze do odczytania niż case' s statement answer. Zauważ również, że będzie to znacznie wolniejsze niż użycie instrukcji case, jak zauważył Paul, nie używaj jej w pętli.

 8
Author: Samuel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-12-31 22:32:14
[[ $string == *foo* ]] && echo "It's there" || echo "Couldn't find"
 7
Author: Jahid,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-28 19:55:58

Jeden to:

[ $(expr $mystring : ".*${search}.*") -ne 0 ] && echo 'yes' ||  echo 'no'
 7
Author: chemila,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-02-13 17:31:54

grep -q jest przydatny w tym celu.

To samo za pomocą awk:

string="unix-bash 2389"
character="@"
printf '%s' "$string" | awk -vc="$character" '{ if (gsub(c, "")) { print "Found" } else { print "Not Found" } }'

Wyjście:

Nie Znaleziono

string="unix-bash 2389"
character="-"
printf '%s' "$string" | awk -vc="$character" '{ if (gsub(c, "")) { print "Found" } else { print "Not Found" } }'

Wyjście:

Znaleziono

Oryginalne źródło: http://unstableme.blogspot.com/2008/06/bash-search-letter-in-string-awk.html

 4
Author: nyuszika7h,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-28 16:54:12

Lubię sed.

substr="foo"
nonsub="$(echo "$string" | sed "s/$substr//")"
hassub=0 ; [ "$string" != "$nonsub" ] && hassub=1

Edit, Logic:

  • Użyj sed, aby usunąć instancję podłańcucha z łańcucha

  • Jeśli nowy łańcuch różni się od Starego, istnieje podłańcuch

 4
Author: ride,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-05 14:42:02

Mój .bash_profile i jak korzystałem z grepa jeśli ścieżka zawiera Moje 2 bin dirs, nie dołączaj ich

# .bash_profile
# Get the aliases and functions
if [ -f ~/.bashrc ]; then
    . ~/.bashrc
fi

U=~/.local.bin:~/bin

if ! echo "$PATH" | grep -q "home"; then
    export PATH=$PATH:${U}   
fi
 4
Author: Leslie Satenstein,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-14 05:36:44

Uznałem, że ta funkcjonalność jest potrzebna dość często, więc używam domowej funkcji powłoki w moim .bashrc, takiej jak ta, która pozwala mi ponownie używać jej tak często, jak muszę, z łatwą do zapamiętania nazwą: {]}

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

Aby sprawdzić czy $string1 (powiedzmy, abc) jest zawarte w $string2 (powiedzmy, 123abcabc) wystarczy uruchomić stringinstring "$string1" "$string2" i sprawdzić wartość zwracaną, na przykład

stringinstring "$str1" "$str2"  &&  echo YES  ||  echo NO
 3
Author: Kurt Pfeifle,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-11 18:09:46

Spróbuj Oobash jest to biblioteka ciągów w stylu oo dla bash 4. Ma poparcie dla niemieckich umlautów. Jest napisany w bash. Dostępnych jest wiele funkcji: -base64Decode, -base64Encode, -capitalize, -center, -charAt, -concat, -contains, -count, -endsWith, -equals, -equalsIgnoreCase, -reverse, -hashCode, -indexOf, -isAlnum, -isAlpha, -isAscii, -isDigit, -isEmpty, -isHexDigit, -isLowerCase, -isSpace, -isPrintable, -isUpperCase, -isVisible, -lastIndexOf, -length, -matches, -replaceAll, -replaceFirst, -startsWith, -substring, -swapCase, -toLowerCase, -toString, -toUpperCase, -trim, i -zfill.

Zobacz też przykład:

[Desktop]$ String a testXccc                                                  
[Desktop]$ a.contains tX                   
true                                                           
[Desktop]$ a.contains XtX      
false      

Oobash jest dostępny w Sourceforge.net .

 2
Author: andreas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-08 15:03:21

Dokładne dopasowanie słów:

string='My long string'
exactSearch='long'

if grep -E -q "\b${exactSearch}\b" <<<${string} >/dev/null 2>&1
  then
    echo "It's there"
  fi
 2
Author: Eduardo Cuomo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-10 14:56:18

Używam tej funkcji(jedna zależność nie jest uwzględniona, ale oczywista). Przechodzi testy pokazane poniżej. Jeżeli funkcja zwraca wartość > 0, wtedy łańcuch został znaleziony. Możesz równie łatwo zwrócić 1 lub 0 zamiast tego.

function str_instr {
   # Return position of ```str``` within ```string```.
   # >>> str_instr "str" "string"
   # str: String to search for.
   # string: String to search.
   typeset str string x
   # Behavior here is not the same in bash vs ksh unless we escape special characters.
   str="$(str_escape_special_characters "${1}")"
   string="${2}"
   x="${string%%$str*}"
   if [[ "${x}" != "${string}" ]]; then
      echo "${#x} + 1" | bc -l
   else
      echo 0
   fi
}

function test_str_instr {
   str_instr "(" "'foo@host (dev,web)'" | assert_eq 11
   str_instr ")" "'foo@host (dev,web)'" | assert_eq 19
   str_instr "[" "'foo@host [dev,web]'" | assert_eq 11
   str_instr "]" "'foo@host [dev,web]'" | assert_eq 19
   str_instr "a" "abc" | assert_eq 1
   str_instr "z" "abc" | assert_eq 0
   str_instr "Eggs" "Green Eggs And Ham" | assert_eq 7
   str_instr "a" "" | assert_eq 0
   str_instr "" "" | assert_eq 0
   str_instr " " "Green Eggs" | assert_eq 6
   str_instr " " " Green "  | assert_eq 1
}
 1
Author: Ethan Post,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-11 02:09:50

Bash4 + przykłady. Uwaga: nieużywanie cudzysłowów spowoduje problemy, gdy słowa zawierają spacje itp.. Zawsze cytuję w bash IMO.

Oto kilka przykładów BASH4+:

Przykład 1, zaznaczenie "tak" w łańcuchu znaków (bez rozróżniania wielkości liter):

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

Przykład 2, zaznaczenie "tak" w łańcuchu znaków (bez rozróżniania wielkości liter):

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

Przykład 3, Zaznacz "tak" w łańcuchu znaków (rozróżnia wielkość liter):

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

Przykład 4, Zaznacz "tak" w łańcuchu znaków (rozróżnia wielkość liter):

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

Przykład 5, dokładne dopasowanie (wielkość liter):

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

Przykład 6, dokładne dopasowanie (niewrażliwe na wielkość liter):

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

Przykład 7, dokładne dopasowanie:

     if [ "$a" = "$b" ] ;then
Smacznego.
 0
Author: Mike Q,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-10-05 18:54:36