Как проверить, содержит ли строка подстроку в Bash

У меня есть строка в Bash:

 string="My string"
 

Как проверить, содержит ли она еще одну строку?

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

Где ?? - мой неизвестный оператор. Я использую эхо и grep ?

 if echo "$string" | grep 'foo'; then
  echo "It's there!"
fi
 

Это выглядит немного неуклюжим.

вопрос задан 23.10.2008
davidsheldon
10891 репутация

20 ответов


  • 2605 рейтинг

    Вы можете использовать ответ Маркуса (* подстановочные знаки) за пределами оператора case, если вы используете двойные скобки:

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

    Обратите внимание, что пробелы в игольной веревке должны быть помещены между двойными кавычками, а * подстановочные знаки должны быть снаружи.

    ответ дан Adam Bellaire, с репутацией 77640, 23.10.2008
  • 397 рейтинг

    Если вы предпочитаете подход регулярного выражения:

     string='My string';
    
    if [[ $string =~ .*My.* ]]
    then
       echo "It's there!"
    fi
     
    ответ дан Matt Tardiff, с репутацией 4146, 23.10.2008
  • 245 рейтинг

    Я не уверен в использовании оператора if, но вы можете получить аналогичный эффект с аргументом case:

     case "$string" in 
      *foo*)
        # Do stuff
        ;;
    esac
     
    ответ дан Marcus Griep, с репутацией 5342, 23.10.2008
  • 147 рейтинг

    Совместимый ответ

    Поскольку уже есть много ответов с использованием специфичных для Bash функций, есть способ работать с более оболочками, такими как :

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

    На практике это может дать:

     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
     

    Это было проверено под , , и (busybox), и результат всегда:

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

    В одну функцию

    По просьбе @EeroAaltonen вот версия той же демонстрации, протестированная под теми же оболочками:

     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
    }
     

    Затем:

     $ 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'.
     

    Обратите внимание: вам нужно избегать или дублировать кавычки и / или двойные кавычки:

     $ 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'.
     

    Простая функция

    Это было проверено в , и, конечно же, :

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

    Это все люди!

    То теперь:

     $ 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
     

    ... Или, если поданная строка может быть пуста, как указано в @Sjlver, функция будет выглядеть следующим образом:

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

    или, как было предложено замечанием Адриана Гюнтера , избегая -o переключателей:

     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
     

    С пустыми строками:

     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
     
    ответ дан F. Hauri, с репутацией 33584, 8.12.2013
  • 119 рейтинг

    Вы должны помнить, что сценарий оболочки меньше языка и больше набора команд. Инстинктивно вы думаете, что этот «язык» требует от вас следовать if с [ или [[ . Обе эти команды - это просто команды, которые возвращают статус выхода, указывающий на успех или неудачу (как и любая другая команда). По этой причине я бы использовал grep , а не [ команду.

    Просто делать:

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

    Теперь, когда вы думаете о if проверяете статус выхода команды, которая следует за ним (в комплекте с точкой с запятой). Почему бы не пересмотреть источник строки, которую вы тестируете?

     ## 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
    #...
     

    Опция -q делает grep ничего не выводить, поскольку нам нужен только код возврата. <<< заставляет оболочку расширять следующее слово и использовать его как вход в команду, однострочную версию документа [ здесь (я не уверен, является ли это стандартом или башизмом).

    ответ дан Mark Baker, с репутацией 4370, 27.10.2008
  • 75 рейтинг

    Принятый ответ лучше, но поскольку есть несколько способов сделать это, вот еще одно решение:

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

    ${var/search/replace} - $var при этом первый экземпляр search заменен на replace , если он найден (он не изменяется $var ). Если вы попытаетесь заменить foo ничем, и строка изменилась, то, очевидно, foo было найдено.

    ответ дан ephemient, с репутацией 149435, 23.10.2008
  • 33 рейтинг

    Таким образом, есть много полезных решений для вопроса - но который быстрее или использует наименее ресурс?

    Повторные тесты с использованием этого кадра:

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

    Замена TEST каждый раз:

     [[ $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 был в ответе Ф. Хури)

    И для хихиканья:

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

    Таким образом, простой вариант заместителя предикативно выигрывает в расширенном тесте или случае. Случай переносимый.

    Трубопроводы до 100000 град предсказуемо болезненны! Старое правило об использовании внешних утилит не требуется.

    ответ дан Paul Hedderly, с репутацией 2682, 27.08.2014
  • 23 рейтинг

    Это также работает:

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

    И отрицательный тест:

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

    Я полагаю, что этот стиль немного более классический, менее зависимый от особенностей оболочки Bash.

    Параметр -- представляет собой чистую POSIX-паранойю, используемую для защиты от входных строк, подобных параметрам, например --abc или -a .

    Примечание. В замкнутом цикле этот код будет намного медленнее, чем использование внутренних функций оболочки Bash, поскольку один (или два) отдельных процесса будут созданы и подключены через каналы.

    ответ дан kevinarpe, с репутацией 9786, 1.12.2012
  • 12 рейтинг

    Как насчет этого:

     text="   <tag>bmnmn</tag>  "
    if [[ "$text" =~ "<tag>" ]]; then
       echo "matched"
    else
       echo "not matched"
    fi
     
    ответ дан Mark Baker, с репутацией 4370, 9.02.2009
  • 9 рейтинг

    Этот ответ переполнения стека был единственным, кто улавливал пробелы и тире:

     # For null cmd arguments checking   
    to_check=' -t'
    space_n_dash_chars=' -'
    [[ $to_check == *"$space_n_dash_chars"* ]] && echo found
     
    ответ дан Yordan Georgiev, с репутацией 2420, 26.08.2013
  • 8 рейтинг

    Как сказал Павел в своем сравнении производительности:

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

    Это POSIX-совместимый, как «case» $ string «in», предоставленный Marcus, но его немного легче читать, чем ответ на запрос case. Также обратите внимание, что это будет намного медленнее, чем использование аргумента case, как указал Павел, не используйте его в цикле.

    ответ дан Samuel, с репутацией 3311, 31.12.2014
  • 7 рейтинг

    Один:

     [ $(expr $mystring : ".*${search}.*") -ne 0 ] && echo 'yes' ||  echo 'no'
     
    ответ дан chemila, с репутацией 2278, 11.11.2011
  • 7 рейтинг
    [[ $string == *foo* ]] && echo "It's there" || echo "Couldn't find"
    
    ответ дан Jahid, с репутацией 12661, 28.04.2015
  • 4 рейтинг

    grep -q полезен для этой цели.

    То же самое с использованием awk :

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

    Вывод:

    не обнаружена

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

    Вывод:

    Найденный

    Оригинальный источник: http://unstableme.blogspot.com/2008/06/bash-search-letter-in-string-awk.html

    ответ дан Mark Baker, с репутацией 4370, 21.12.2008
  • 4 рейтинг

    Мой .bash_profile и как я использовал grep, если PATH включил мои 2 bin dirs, не добавляйте их

     # .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
     
    ответ дан Leslie Satenstein, с репутацией 144, 14.01.2017
  • 4 рейтинг

    Мне нравится sed.

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

    Редактировать, логика:

    • Использовать sed для удаления экземпляра подстроки из строки

    • Если новая строка отличается от старой строки, существует подстрока

    ответ дан ride, с репутацией 81, 3.03.2016
  • 3 рейтинг

    Я нашел эту функцию довольно часто, поэтому я использую домашнюю функцию оболочки в моем .bashrc как это, что позволяет мне использовать ее так часто, как мне нужно, с легко запоминаемым именем:

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

    Чтобы проверить, содержится ли $string1 (скажем, abc ) в $string2 (скажем, 123abcABC ), мне просто нужно запустить stringinstring "$string1" "$string2" и проверить возвращаемое значение, например

     stringinstring "$str1" "$str2"  &&  echo YES  ||  echo NO
     
    ответ дан Kurt Pfeifle, с репутацией 62066, 1.07.2012
  • 2 рейтинг

    Попробуйте oobash, это строковая библиотека стиля OO для bash 4. У нее есть поддержка немецких умляутов. Это написано в bash. Доступны многие функции: -base64Decode , -base64Encode , -capitalize , -center , -charAt , -concat , -contains , -count , -endsWith , -equals , -base64Encode , -base64Encode , -base64Encode , -base64Encode , -base64Encode , -base64Encode , -base64Encode , -base64Encode , -base64Encode , -base64Encode , -capitalize , -capitalize , -capitalize , -capitalize , -capitalize , -capitalize , -capitalize , -capitalize , -capitalize , -capitalize , -center , -center , -center , -center , -center , -center , -center и -center .

    Посмотрите пример с примерами:

     -center 

    oobash доступен на Sourceforge.net .

    ответ дан andreas, с репутацией 53, 27.08.2010
  • 2 рейтинг

    Точное совпадение слов:

     string='My long string'
    exactSearch='long'
    
    if grep -E -q "\b${exactSearch}\b" <<<${string} >/dev/null 2>&1
      then
        echo "It's there"
      fi
     
    ответ дан Eduardo Cuomo, с репутацией 9021, 10.11.2016
  • 1 рейтинг

    Я использую эту функцию (одна зависимость не включена, но очевидна). Он проходит тесты, показанные ниже. Если функция возвращает значение> 0, то строка была найдена. Вы могли бы так же легко вернуть 1 или 0 вместо этого.

     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
    }
     
    ответ дан Ethan Post, с репутацией 1929, 11.04.2018