Вопрос: Строка содержит подстроку в 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

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


1705


источник


Ответы:


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

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

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


2424



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

string='My string';

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

378



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

case "$string" in 
  *foo*)
    # Do stuff
    ;;
esac

234



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

Поскольку ответов с использованием 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переключатель:

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

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

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

130



Вы должны помнить, что сценарий оболочки меньше языка и больше набора команд. Инстинктивно вы думаете, что этот «язык» требует от вас следовать 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 не выводит ничего, поскольку нам нужен только код возврата. <<<заставляет оболочку расширять следующее слово и использовать его в качестве ввода команды, однострочную версию <<здесь документ (я не уверен, является ли это стандартом или башизмом).


106



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

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

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


72



So there are lots of useful solutions to the question - but which is fastest / uses the least resource?

Repeated tests using this frame:

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

Replacing TEST each time:

[[ $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 was in F. Houri's answer)

And for giggles:

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

So the simple substituion option predicatbly wins whether in an extended test or a case. The case is portable.

Piping out to 100000 greps is predictably painful! The old rule about using external utilities without need holds true.


31



This also works:

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

And the negative test is:

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

I suppose this style is a bit more classic -- less dependent upon features of Bash shell.

The -- argument is pure POSIX paranoia, used to protected against input strings similar to options, such as --abc or -a.

Note: In a tight loop this code will be much slower than using internal Bash shell features, as one (or two) separate processes will be created and connected via pipes.


22



How about this:

text="   <tag>bmnmn</tag>  "
if [[ "$text" =~ "<tag>" ]]; then
   echo "matched"
else
   echo "not matched"
fi

11



This Stack Overflow answer was the only one to trap space and dash chars:

# For null cmd arguments checking   
to_check=' -t'
space_n_dash_chars=' -'
[[ $to_check == *"$space_n_dash_chars"* ]] && echo found

8



One is:

[ $(expr $mystring : ".*${search}.*") -ne 0 ] && echo 'yes' ||  echo 'no'

7



[[ $string == *foo* ]] && echo "It's there" || echo "Couldn't find"

6