Как извлечь подстроку из имени каталога, используя bash - PullRequest
0 голосов
/ 19 февраля 2020

У меня есть группа каталогов со следующим шаблоном имени:

[случайная комбинация чисел и / или символов и / или символов (включая пробел)] + [один пробел] + [(] + [число] + [)].

Например, asdf%k123 test1 (12345) foo(123)??bar (456) 324(asd)! (456) random()123 (456)

Как я go могу извлечь только число в скобках, используя bash команды (без '()')? Имейте в виду, что число в скобках может встречаться в произвольной части имени, но мне нужно только в конце. В любом случае каждое имя оканчивается на [пробел] [(] [число] [)].

Ответы [ 2 ]

0 голосов
/ 20 февраля 2020

Не могли бы вы попробовать следующее:

pat='[[:blank:]]\(([[:digit:]]+)\)/$'   # regex pattern explained below
path="."                                # or specify to the path where the directories exist
for d in "$path"/*/; do                 # pick directories in the $path
    if [[ $d =~ $pat ]]; then           # if the directory name matches the pattern
        echo "${BASH_REMATCH[1]}"       # then print the extracted number
    fi
done

Результат с приведенными примерами:

456
12345
456
456

Шаблон регулярного выражения [[:blank:]]\(([[:digit:]]+)\)/$ соответствует подстроке, такой как:

  • пробел [[:blank:]]
  • , за которым следует левая часть \(
  • , за которой следует последовательность из одной или нескольких цифр ([[:digit:]]+)
  • , за которой следует правая часть \)
  • , за которой следует sla sh (что означает, что это каталог) /
  • и конец строки $

Последовательность цифр окружена паренами, поэтому совпадающая часть захватывается и присваивается переменной bash BASH_REMATCH.

0 голосов
/ 19 февраля 2020

Получить номер

Последний номер можно получить с помощью sed:

sed 's/.*(\([0-9]*\))$/\1/' <<< "asdf%k123 test1 (12345)"

12345

Сценарий sed написан так:

  • начало s означает, что это выполняет подстановку, которая использует следующий синтаксис s/pattern/replacement/
  • символ / определяет разделитель для замены, вы можете использовать любой символ, но / является очень распространенным
  • .*(\([0-9]*\))$ - это шаблон (я вернусь к нему позже)
  • \1 является заменой, в этом случае она заменяет первую строку, захваченную шаблоном, который является числом (см. ниже)

Ключевым элементом является шаблон. В этом случае .*(\([0-9]*\))$ можно разделить на:

  • .*, который соответствует любому символу любое число раз
  • (, который соответствует открывающей скобке
  • \([0-9]*\), которая захватывает любую строку, состоящую из цифр, или число
  • ), которое соответствует символу закрывающей скобки
  • $, которое соответствует концу строки

В схеме захвата, то есть (\([0-9]*\), следует отметить, что \( и \) являются разделителями для захвата и не должны быть ошибочно приняты с ( и ), которые являются обычными скобками символов.

TL; DR: этот шаблон говорит: «Я хочу записать число в круглых скобках непосредственно перед концом строки». А сценарий sed говорит: «Я хочу напечатать исключительно номер, который был захвачен».

Список каталогов

Вы можете проанализировать каталоги, используя find. Если вы просто хотите, чтобы подпапки первого уровня:

find /path/to/dir -mindepth 1 -maxdepth 1 -type d

Опция -mindepth 1 -maxdepth 1 гарантирует, что вы получаете только детей 1 глубины, тогда как опция -type d перечисляет только каталоги ( не файлы, а не символы c ссылки и т. д. c.).

Вы можете получить больше глубины, заменив -maxdepth 1 выбранным номером, или просто пропустите эту опцию, чтобы рекурсивно получить все подпапки .

Поскольку в ваших каталогах, похоже, есть все виды специальных символов, я бы также предложил получить их с опцией -print0, которая разделяет результат с нулевым символом \0 вместо новой строки.

Решение

В целом это будет выглядеть так:

find /path/to/dir -mindepth 1 -maxdepth 1 -type d -print0 |
  while IFS= read -r -d '' dirname
  do
    sed 's/.*(\([0-9]*\))$/\1/' <<< $dirname
  done

Если вы хотите отфильтровать каталоги, которые не соответствуют вашему шаблону, Вы можете изменить sed для использования опции -n, а затем распечатать с помощью команды p:

find /path/to/dir -mindepth 1 -maxdepth 1 -type d -print0 |
  while IFS= read -r -d '' dirname
  do
    sed -n 's/.*(\([0-9]*\))$/\1/p' <<< $dirname
  done
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...