36

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

25

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

string="My string"

Как я могу проверить, содержит ли она другую строку?

Я пробовал написать так:

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

Где ?? — это мой неизвестный оператор. Использовать ли мне echo и grep?

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

Это выглядит немного громоздко. как я могу сделать это более элегантным способом?

5 ответ(ов)

10

Если вы предпочитаете подход с использованием регулярных выражений, вы можете сделать это следующим образом:

string='My string'

if [[ $string =~ "My" ]]; then
   echo "Присутствует!"
fi

Обратите внимание, что в данной конструкции используется проверка на соответствие строки с регулярным выражением внутри двойных квадратных скобок [[ ]]. Если подстрока "My" найдена, то выполняется команда echo с сообщением "Присутствует!".

4

Вы не уверены в использовании оператора if, но вы можете добиться аналогичного эффекта с помощью оператора case:

case "$string" in 
  *foo*)
    # Выполнить нужные действия
    ;;
esac

Этот подход проверяет, содержит ли строка "$string" подстроку foo, и выполняет соответствующий блок кода, если условие истинно.

1

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

Просто сделайте так:

if grep -q foo <<<"$string"; then
    echo "Это там"
fi

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

## Вместо этого
filetype="$(file -b "$1")"
if grep -q "tar archive" <<<"$filetype"; then
#...

## Просто сделайте это
if file -b "$1" | grep -q "tar archive"; then
#...

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

1

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

if [ "$string" != "${string/foo/}" ]; then
    echo "Слово найдено!"
fi

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

0

Вопрос очень актуален, так как производительность и использование ресурсов — важные аспекты при выборе метода решения. Давайте посмотрим на результаты тестов, которые вы привели, и сравним их.

Используя следующую команду для тестирования:

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

Если подставить разные методы проверки наличия подстроки, то получается следующее:

  1. [[ $b =~ $a ]] — 2.92 user, 0.06 system, 0:02.99 elapsed
  2. [ "${b/$a//}" = "$b" ] — 3.16 user, 0.07 system, 0:03.25 elapsed
  3. [[ $b == *$a* ]] — 1.85 user, 0.04 system, 0:01.90 elapsed
  4. case $b in *$a):;;esac — 1.80 user, 0.02 system, 0:01.83 elapsed
  5. doContain $a $b — 4.27 user, 0.11 system, 0:04.41 elapsed

И немного забавы:

echo $b|grep -q $a

— 12.68 user, 30.86 system, 3:42.40 elapsed (что, безусловно, довольно болезненно!)

Как видно из результатов, использование простого замещения (шаблон замены) занимает меньше всего времени. Также, вариант с case имеет свои преимущества в портативности. В то время как использование внешнего утилита grep значительно замедляет выполнение, что подтверждает правило — избегайте использования внешних утилит без необходимости.

В итоге, если вы хотите добиться максимальной производительности при проверке наличия подстроки, стоит рассмотреть использование синтаксиса [[ $b == *$a* ]] или case, так как они показывают наилучшие результаты.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь