Гид по технологиям

Поиск подстрок в Bash: простые и надёжные методы

• 8 min read • Linux • Обновлено 20 Nov 2025
Поиск подстрок в Bash: методы и примеры
Поиск подстрок в Bash: методы и примеры

Ноутбук с Linux и приглашением bash

Быстрые ссылки

  • Зачем это полезно?

  • Поиск подстроки с встроенными средствами Bash

  • Поиск подстроки с grep

  • Поиск подстроки с sed

  • Остановить поиск — найдено

Зачем это полезно?

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

В Linux есть много инструментов для работы с текстом: некоторые функциональности встроены в shell, другие реализованы отдельными программами. Для простых проверок лучше применять минимально необходимое средство: это уменьшает зависимость от внешних утилит и повышает читаемость.

Некоторые файлы в системе — не обычные файлы, а интерфейсы к данным (например, /proc или /sys). Входные и выходные данные команд в терминале тоже — текст, что позволяет направлять, фильтровать и комбинировать вывод через каналы (pipe) и перенаправления.

Ниже — набор простых, надёжных приёмов, которые удобно использовать в своих скриптах.

Поиск подстроки с встроенными средствами Bash

Встроенный тест в двойных скобках ([[ … ]]) поддерживает шаблоны (globbing). Самый распространённый приём — обернуть подстроку звёздочками:

if [[ "monkey" = *"key"* ]]; then
  echo "key is in monkey"
else
  echo "key is not in monkey"
fi

Сохраните как double.sh (в исходном примере). Не забудьте сделать файл исполняемым:

chmod +x double.sh

Запуск:

./double.sh

Почему это работает: символ * в шаблоне соответствует любой последовательности символов (включая пустую). Если key встречается внутри строки, весь шаблон совпадёт.

Пример с переменными (double2.sh):

#!/bin/bash

string="Monkey"

substring="key"

if [[ $string = *$substring* ]]; then
  echo "$substring was found in $string"
else
  echo "$substring was not found in $string"
fi

Запуск как

./double2.sh

Советы по надёжности и кавычкам:

  • Внутри [[ … ]] нет разбиения на слова (word splitting) и нет расширения пути (pathname expansion) для строк в левой части, но шаблоны справа всё ещё обрабатываются. Часто безопаснее писать
if [[ $string == *"$substring"* ]]; then
  ...
fi

звёздочки остаются как часть шаблона, а сама переменная подстроки взята в кавычки — это предотвращает неожиданное разбиение при наличии пробелов и сохраняет поведение шаблона.

  • Если подстрока может содержать символы шаблона (например *, ?, [), и вы хотите искать именно литеральную последовательность, не используйте необработанный шаблон. Для литерального поиска применяйте grep -F или экранируйте метасимволы (см. разделы ниже).

Функция для переиспользования (double3.sh):

#!/bin/bash

shopt -s nocasematch

string="Monkey"

substring="Key"

capital="London"

check_substring ()
{
  if [[ $1 = *$2* ]]; then
    echo "$2 was found in $1"
  else
    echo "$2 was not found in $1"
  fi
}

check_substring "Monkey" "key"
check_substring $string $substring
check_substring $string "banana"
check_substring "Wales" $capital

Запуск:

./double3.sh

Запуск скрипта double3.sh в терминале

Примечание о регистре: shopt -s nocasematch делает сравнения в [[…]] нечувствительными к регистру.

Использование case:

#!/bin/bash

shopt -s nocasematch

string="Wallaby"

substring="Wall"

case $string in
  *$substring*)
    echo "$substring was found in $string"
    ;;
  *)
    echo "Nothing matched: $string"
    ;;
esac

case особенно удобен, когда нужно сопоставить строку с множеством вариантов. Шаблоны в case читаемы и просты для отладки.

Важно: оператор [[ … ]] является расширением Bash (и некоторых других современных шеллов), минимально совместимый с POSIX sh — не всегда. Если требуется совместимость с /bin/sh на старых системах, используйте portable-подходы (grep/awk) или проверьте доступность Bash.


Советы по безопасности и устойчивости в скриптах:

  • Избегайте echo $var без кавычек: используйте printf ‘%s’ “$var” или echo “$var”.
  • Если переменные берутся из внешнего ввода, учитывайте возможность инъекций в командные подстановки и аккуратно экранируйте или избегайте их.

Поиск подстроки с grep

grep — стандартный инструмент для поиска. Для простых литеральных совпадений лучше использовать фиксированный режим (-F), а для тихого обнаружения — опцию -q.

Пример subgrep.sh:

#!/bin/bash

string="porridge pot"

substring="ridge"

if $(echo $string | grep -q $substring); then
  echo "$substring was found in $string"
else
  echo "$substring was not found in $string"
fi

Улучшенная и надёжная версия (рекомендуется):

#!/bin/bash

string="porridge pot"
substring="ridge"

if printf '%s' "$string" | grep -Fq -- "$substring"; then
  echo "$substring was found in $string"
else
  echo "$substring was not found in $string"
fi

Пояснения:

  • printf ‘%s’ “$string” безопаснее, чем echo $string: он не интерпретирует опции и сохраняет пробелы.
  • grep -Fq ищет фиксированную строку (-F) и не печатает вывод (-q). Флаг – защищает от случаев, когда подстрока начинается с дефиса (-).
  • grep возвращает код выхода: 0 при совпадении, 1 при отсутствии, >1 при ошибке.

Когда использовать grep:

  • Если подстрока может содержать regex-метасимволы и вы не хотите их интерпретировать — используйте -F.
  • Если нужно использовать регулярные выражения — используйте без -F или с -E для расширенных regex.

Поиск подстроки с sed

sed часто применяется для обработки строк и может помочь при проверке наличия подстроки.

Шаблон, который печатает строки, содержащие подстроку:

"/$substring/p"

Пример subsed.sh:

#!/bin/bash

string="Sweden"

substring="eden"

if [ -n "$(sed -n "/$substring/p" <<< $string)" ]; then
  echo "$substring was found in $string"
else
  echo "$substring was not found in $string"
fi

Рекомендации и улучшения:

  • Используйте кавычки с here-string: <<< “$string” — это сохранит пробелы и специальные символы.
  • Если подстрока содержит символ /, смените разделитель в sed, например sed -n "\@$substring@p" (символ @ вместо /) или предварительно экранируйте слэши.
  • sed интерпретирует регулярные выражения; если требуется только литеральный поиск, grep -F может быть проще.

Улучшенная версия с безопасными кавычками и альтернативным разделителем:

#!/bin/bash

string="dir/with/slash"
substring="dir/with"

if [ -n "$(sed -n "\@$substring@p" <<< "$string")" ]; then
  echo "$substring was found in $string"
else
  echo "$substring was not found in $string"
fi

Другие инструменты: awk и Perl

  • awk: простая проверка awk '/pattern/ { found=1 } END { exit !found }' — полезно, если нужно обрабатывать поля в строках.
  • Perl: для сложных задач с Unicode и экранированием используйте Perl-совместимые механизмы: perl -ne 'exit 0 if /quotemeta($ARGV[0])/' -- "$substring" — но Perl обычно излишен для одноразовой проверки.

Пример awk-клипа:

if printf '%s' "$string" | awk -v s="$substring" 'index($0,s){exit 0} END{exit 1}'; then
  echo "found"
fi

Когда встроенные средства не подходят — типичные кейсы

  • Подстрока содержит символы шаблона (*, ?, [) — если нужен буквальный поиск, используйте grep -F или заранее экранируйте.
  • Работаете в чистом POSIX sh без [[ ]] — используйте grep/awk.
  • Нужна поддержка сложных Unicode-символов и нормализации — рассмотрите Perl или утилиты с поддержкой Unicode.

Примеры неочевидных ошибок и как их избежать

  1. Проблема: переменная содержит пробелы

Неправильно:

if [[ $string = *$substring* ]]; then ...

Правильно:

if [[ $string == *"$substring"* ]]; then ...
  1. Проблема: подстрока начинается с дефиса -

Неправильно передавать как аргумент grep без --.

Исправление:

printf '%s' "$string" | grep -Fq -- "$substring"
  1. Проблема: подстрока содержит / при использовании sed с / как разделителем

Решение: смените разделитель или экранируйте слэши:

sed -n "\@$substring@p"
  1. Проблема: шаблон интерпретируется как glob при использовании [[ … ]]

Если хотите точное соответствие, избегайте незахищённых шаблонов или используйте grep -F.

Лучшие практики и рекомендации (чек-лист)

Для разработчика скриптов:

  • Всегда ставьте кавычки там, где это важно: “$variable”.
  • Используйте printf вместо echo для пользовательских строк.
  • Предпочитайте grep -Fq для буквального поиска.
  • Для простых проверок используйте [[ $string == “$substr” ]] (Bash).
  • Если подстрока — пользовательский ввод, фильтруйте спецсимволы или используйте безопасные режимы поиска.

Для системного администратора:

  • Проверяйте совместимость со /bin/sh на целевых системах.
  • Если скрипт должен быть переносимым, исходите из минимального набора утилит.
  • Логируйте ситуации, где совпадение не найдено, чтобы отладить возможные проблемы с кодировкой.

Для SRE / CI:

  • Добавьте unit-тесты для граничных случаев (пустая строка, специальные символы, длинные строки).
  • Убедитесь, что локаль (LC_ALL/LC_COLLATE) не влияет на поведение поиска.

Критерии приёмки

  • Скрипт правильно определяет наличие подстроки в следующих случаях: пустая подстрока, подстрока в начале, в середине, в конце.
  • Скрипт корректно обрабатывает пробелы и табуляции внутри строк.
  • Скрипт не ломается, если подстрока содержит glob-метасимволы или слэши.
  • Скрипт работает на целевой оболочке (Bash или sh) и задокументирована требуемая среда выполнения.

Небольшая методология: как выбрать инструмент

  1. Нужен ли буквальный поиск? Если да — grep -Fq.
  2. Нужна ли нечувствительность к регистру? grep -i или shopt -s nocasematch в Bash.
  3. Требуется ли повышение производительности при большом объёме данных? Для потоковых данных используйте grep/awk; для коротких строк — встроенные тесты быстрее.
  4. Нужна ли переносимость на /bin/sh? Если да — не используйте [[ … ]].

Ментальные модели / эвристики

  • “Если задача простая — используй самое простое средство”: для единичной проверки подстроки — Bash [[…]] или grep -F.
  • “Если строка может содержать метасимволы — думай о буквализации”: применяй fixed-mode или экранируй.
  • “Если нужна масштабируемость — используй потоковые утилиты”: grep/awk эффективны для больших объёмов.

Примеры тест-кейсов и приёмочные тесты

Тесты, которые стоит автоматизировать:

  • Поиск “a” в “a” → найдено
  • Поиск “” (пустая) в “anything” → результат зависит от выбранного поведения (обычно считать найденной)
  • Подстрока с пробелом: “a b” в “x a b y” → найдено
  • Подстрока с метасимволами: ““ в “ab” → если нужен буквальный поиск — считать найденным
  • Подстрока со слэшем: “dir/with” в “dir/with/slash” → найдено

Decision flowchart

flowchart TD
  A[Нужно проверить подстроку?] --> B{Буквальный поиск?}
  B -- Да --> C[Использовать grep -Fq или awk index]
  B -- Нет --> D{Требуется regex?}
  D -- Да --> E[Использовать grep -E / sed / Perl]
  D -- Нет --> F{Совместимость с POSIX?}
  F -- Да --> C
  F -- Нет --> G[Можно использовать [[ ... ]] или case в Bash]
  C --> H[Проверить кавычки и -- для дефисов]
  E --> H
  G --> H

Краткий шпаргалка (cheat sheet)

  • Bash (регистр учитывается): [[ $s == “$sub” ]]
  • Bash (игнорировать регистр): shopt -s nocasematch
  • grep фиксированная строка: printf ‘%s’ “$s” | grep -Fq – “$sub”
  • sed: sed -n “\@$sub@p” <<< “$s” (смена разделителя)
  • awk: printf ‘%s’ “$s” | awk -v s=”$sub” ‘index($0,s){exit 0} END{exit 1}’

Контрпримеры / когда это ломается

  • Некорректно экранированные кавычки приведут к ошибкам.
  • Локаль может повлиять на сравнение символов (особенно сортировка/сравнение Unicode).
  • Если строка очень большая, вложенные конструкции и дорогие трансформации могут замедлить.

Безопасность и приватность

  • Не подставляйте необработанные пользовательские строки напрямую в команды без экранирования, особенно если используете конструкцию вида eval или командную подстановку.
  • Для поиска в логах с чувствительной информацией фильтруйте и не выводите секреты в логах.

1‑строчный глоссарий

  • Подстрока — непрерывная последовательность символов внутри строки.
  • Глоб (glob) — шаблон файловой маски (*, ?, []).
  • fixed-mode — режим поиска фиксированной строки (без regex), часто реализуется через grep -F.

Итог

Для большинства повседневных сценариев встроенные возможности Bash ([[…]] и case) или простой grep -Fq обеспечивают достаточную надёжность и читаемость. Если подстрока может содержать специальные символы или требуется совместимость с POSIX sh, отдайте предпочтение grep/awk. Всегда оборачивайте переменные в кавычки и используйте безопасные методы чтения/передачи строк (printf, here-strings с кавычками).

Короткий чек-лист для внедрения в проект

  • Выбрана целевая оболочка (Bash vs sh)
  • Обработаны пробелы и спецсимволы
  • Написаны тесты для граничных случаев
  • Документировано поведение при пустой подстроке
Поделиться: X/Twitter Facebook LinkedIn Telegram
Автор
Редакция

Похожие материалы

Несколько аккаунтов Skype: Multi Skype Launcher
Программное обеспечение

Несколько аккаунтов Skype: Multi Skype Launcher

Журнал для работы: повысить продуктивность
Productivity

Журнал для работы: повысить продуктивность

Персональные звуки уведомлений на Android
Android.

Персональные звуки уведомлений на Android

Скачивание шоу Hulu для офлайн‑просмотра
Стриминг

Скачивание шоу Hulu для офлайн‑просмотра

Microsoft Start: персонализированная новостная лента
Новости

Microsoft Start: персонализированная новостная лента

Как изменить имя в Epic Games быстро
Гайды

Как изменить имя в Epic Games быстро