Вопрос или проблема
Я могу удалить шаблон в переменной bash, используя ${variable##pattern}
(лидирующий) или ${variable%%pattern}
(трейлинг).
Но я не могу найти способ только на bash, чтобы сохранить шаблон и выбросить остальное.
Я знаю, что есть решения с использованием sed
, awk
или grep
, но я хочу знать, есть ли разумно эффективное решение только на bash, которое я упускаю?
PS: Это не просто праздный вопрос. Исходная проблема в том, что я хочу обработать файлы, имя которых содержит шаблон (технически, '[A-Z]+([A-Z])-[0-9][0-9]+([0-9])'
: заглавные буквы, за которыми следует дефис и цифры) и я хочу использовать тот же шаблон, чтобы перечислить файлы и извлечь соответствующую строку для дальнейшей обработки.
${var%"${var##pattern}"}
${var#"${var%%pattern}"}
Пример:
$ k='ab*10cd20ef*'
$ echo "${k%"${k##*[0-9]}"}"
ab*10cd20
$ echo "${k#"${k%%[0-9]*}"}"
10cd20ef*
Обратите внимание, что кавычки важны, чтобы предотвратить интерпретацию оболочки расширений как шаблона. Попробуйте echo "${k#${k%%[0-9]*}}"
, чтобы увидеть, что это приведет к неправильному результату.
В Bash вы также можете использовать регулярные выражения:
#!/bin/bash
re="[A-Z][A-Z]+-[0-9][0-9][0-9]+"
file=foo-BAR-1234.txt
if [[ $file =~ $re ]]; then
echo "имя файла '$file' совпадает, совпадающая часть '${BASH_REMATCH[0]}'"
fi
С file=foo-BAR-1234.txt
это совпадает с частью BAR-1234
и выводит соответствующим образом. Вы также можете использовать скобки в регулярном выражении, чтобы захватить часть шаблона, они будут доступны в ${BASH_REMATCH[1]}
и т. д.
Конечно, обратите внимание, что формат для регулярных выражений отличается от формата расширенных гло́бов Bash/Ksh: вместо +([abc])
вам нужно использовать [abc]+
или ([abc])+
, при этом скобки являются необязательными, когда звездочка применяется только к одной группе скобок. То же самое касается *
и ?
. Также вы можете записать, например, [0-9]{3,}
, для трех и более цифр.
Получение только совпадения шабона регулярного выражения с помощью одного расширения переменной:
Немного поздно, но может быть полезно для кого-то еще. Пример: Я создал это для извлечения нумерации эпизодов из текста телесериала. (что является регулярным выражением с неизвестными числами)
Следующий код содержит некоторые поясняющие комментарии.
#!/bin/bash
#Это для расширенного гло́бирования
shopt -s extglob
#Получение только совпадения шаблона регулярного выражения с помощью расширения переменной
Var="Текст перед совпадением_S02E12_Текст после совпадения"
#Мы хотим только шаблон из эпизода телесериала
MatchPattern="S[0-9][0-9]E[0-9][0-9]"
OnlyTheMatch="${Var//@(${Var//@($MatchPattern)*}|${Var//*@($MatchPattern)})/}"
echo "${OnlyTheMatch}" > Output_OnlyTheMatchPattern.txt
#Сброс расширенного гло́бирования
shopt -u extglob
#Это делает следующее:
#Мы "заменяем" текст до и после на "ничто" ${Var//match/}
#(и тем самым удаляем их)
#${Var//match/} может удалить только одно совпадение, но мы хотим удалить несколько (текст до И после)
#Чтобы использовать несколько шаблонов совпадений, мы должны поместить их в @(..|..) разделенные вертикальными чертами '|'
#OnlyTheMatch="${Var//@(TextBeforeMatch|TextAfterMatch)/}"
#Текст перед совпадением можно найти с помощью расширения переменной: ${Var//@($MatchPattern)*}
#Текст после совпадения также можно найти с помощью расширения переменной: ${Var//*@($MatchPattern)}
#И эти переменные могут обе быть записаны в @(..|..) разделенные вертикальными чертами '|'
#Вместе создавая одно расширение переменной для получения только совпадающего шаблона.
Может быть, это близко к тому, что вы ищете.
Ответ или решение
Как сохранить совпадение шаблона в Bash-расширении переменной
Bash предлагает множество инструментов для работы с переменными и обработки строк, однако сохранить совпадение шаблона, выбрасывая остальную часть строки, может показаться сложной задачей. Рассмотрим, как это можно сделать, используя методы расширения переменной Bash, аналогичные тем, которые вы упомянули.
Введение в расширение переменных Bash
Расширение переменных в Bash предоставляет удобные методы для манипуляции строками. С помощью конструкций вроде ${variable##pattern}
и ${variable%%pattern}
мы можем удалять части строки. Однако чтобы сохранить совпадение шаблона, но удалить остальную часть строки, приходится использовать более сложные конструкции.
Сохранение совпадения шаблона с использованием расширения переменных
Для решения вашей задачи можно воспользоваться следующими подходами:
- Удаление всего, кроме совпадения: Можно использовать механизм на основе специальных символов и переменного расширения, чтобы отсеять ненужные части.
#!/bin/bash
Var="Text before match_S02E12_Text after match"
MatchPattern="S[0-9][0-9]E[0-9][0-9]"
# Включаем расширенные глобусы
shopt -s extglob
# Сохраняем совпадение
OnlyTheMatch="${Var//@(${Var//@($MatchPattern)*}|${Var//*@($MatchPattern)})/}"
# Проверяем результат
echo "${OnlyTheMatch}"
В этом примере мы используем механизм @(pattern)
для определения всех частей строки, которые не соответствуют заданному шаблону, и удаляем их.
Использование базовых регулярных выражений в bash
Помимо методов расширения, полные регулярные выражения также могут быть использованы с помощью [[ ]].
. Например:
#!/bin/bash
re="([A-Z]+-[0-9]+)"
file="foo-BAR-1234.txt"
if [[ $file =~ $re ]]; then
echo "Совпадение в имени файла: '${BASH_REMATCH[0]}'"
fi
Этот подход позволяет находить и выводить совпадение с шаблоном, где используется встроенная поддержка регулярных выражений.
Подходы к обработке имен файлов
Если вы хотите извлечь совпадение из имени файла, содержащего буквы и цифры, приведенное решение можно адаптировать и расширить:
#!/bin/bash
# Задать регулярное выражение
regex="([A-Z]+-[0-9]+)"
for file in *.txt; do
if [[ $file =~ $regex ]]; then
echo "Найдено совпадение в файле '$file': '${BASH_REMATCH[0]}'"
fi
done
Заключение
Хотя в Bash нет встроенной функции, которая бы напрямую сохраняла совпадение шаблона, можно использовать разные подходы для решения этой задачи через расширение переменных и регулярные выражения.
Каждое из предложенных решений подходит для различных сценариев, будь то работа с переменными или обработка имен файлов. Исследуйте, адаптируйте и используйте тот метод, который наилучшим образом соответствует вашим требованиям.