Заменить символы в захватывающей группе с использованием sed

Вопрос или проблема

У меня есть это, что почти делает то, что я хочу

git show-branch --current $SHA1 \
  | sed --quiet --regexp-extended \
    's/^.*\* \[[a-z]+\/(B-[0-9]+)-([a-z0-9-]+)\].*/\1 \2/p' \
  | sed --quiet 2p

и выводит

B-47120 java-11-take2

git show-branch выводит это

! [62cba3e2b3ba8e1115bceba0179fea6c569d9274] B-48141 remove env prefix
 * [ccushing/B-47120-java-11-take2] B-48141 remove env prefix
--
 * [ccushing/B-47120-java-11-take2] B-48141 remove env prefix
+* [62cba3e2b3ba8e1115bceba0179fea6c569d9274] B-48141 remove env prefix

желаемый вывод

B-47120 java 11 take2

что заменяет - во второй группе захвата, но я не могу придумать способ заменить - только во второй части, так как мне нужно также захватить её.

Я использую git для windows и ограничен инструментами, предоставляемыми с ним. Хотя, вероятно, есть более программные способы сделать это (я мог бы использовать Perl), я хотел бы увидеть ответ с использованием sed, если это возможно.

ОБНОВЛЕНИЕ

потому что, видимо, это не очевидно. Шаблон представляет собой username/ticketid-description (минус то, что требуется, чтобы получить это из git show-branch).

  • имя пользователя всегда должно быть буквенным
  • ticketid всегда в формате B-\d+
  • описание (\2 может содержать произвольное количество -, которые следует преобразовать в пробелы

Я пытаюсь программно превратить это в заголовочную строку git для хуков prepare-commit-msg git.

Попробуйте следующее:

... |sed -nE '/^[[:blank:]*]+\[[^/]*\/(B-[0-9]+)-([^]]*)\].*/{
             s//\1 \2/; :a s/(B-[0-9]+ [^-]*)-(.*)/\1 \2/;ta; p; }'

Советы:

  • [^X]*: совпадает с любым символом кроме символа X (а также, исключением \nовая строка).
  • [^X]*X: совпадает с любым символом кроме символа X (а также \nовая строка) с последующим символом X.
  • (...): захватывает групповой матч с обратными ссылками \1 для первого, \2 для второго и так далее (вы можете использовать до \9 обратных ссылок).
  • :label s/find/replace/; t label: переход к label при успешной замене и повторение команды.

Или немного короче (в зависимости от формата ввода):

... |sed -nE '/^[^/]*\/([^]]*)\].*/{
             s//\1/; :a s/(B-[0-9]+)(.*)-([^-]*)$/\1\2 \3/;ta; p; }'

Я предлагаю следующее, предполагая несколько вещей, упомянутых ниже (сообщите, если эти предположения неверны):

# выбрать строку с символом слэша "https://unix.stackexchange.com/"
# заменить первый '-' на другой недопустимый символ, например '@'
# заменить все '-' на пробелы
# заменить '@' на '-'
# захватить все между "https://unix.stackexchange.com/" и ']', удалить все остальное
$ sed -n -e '\:/:{s:-:@:;s:-: :g;s:@:-:;s:^.*/\([^]]*\).*:\1:p;q}'
B-47120 java 11 take2

\:/: выбрать строки, содержащие слэш /.

* [ccushing/B-47120-java-11-take2] B-48141 remove env prefix

s:-:@: заменить - на @, не жадно, поэтому только первый.

* [ccushing/B@47120-java-11-take2] B-48141 remove env prefix

s:-: :g заменить - на пробел, жадно.

* [ccushing/B@47120 java 11 take2] B 48141 remove env prefix

s:@:-: заменить @ на -

* [ccushing/B-47120 java 11 take2] B 48141 remove env prefix

s:^.*/\([^]]*\).*:\1:p;q захват между / и ], удаление всего остального, печать и выход.

B-47120 java 11 take2

С конструкциями GNU sed вы можете получить вывод, как показано.

Примечание: я не изменял ваши регулярные выражения, только подправил некоторые части.
Измените {2} на {4}, если, допустим, вам понадобится четвертое совпадение.

sed  --quiet --regexp-extended '
  s/^.*\* \[[a-z]+\/(B-[0-9]+)-([a-z0-9-]+)\].*/\1 \2/;T;G
  s/\n{2}$//;Ta
  s/-/ /2gp;q
  :a;z;H
' file

Это более универсальный скрипт, но я пытался решить аналогичную проблему. Ответ заключается в части s/-/ /2g однострочника. Начинайте со второго экземпляра, а затем замените остальные глобально. Это официально неопределенное поведение, но работает в GNU sed.

sed -n -E -e ‘s/-/ /2g’ -e ‘s/^.[[a-z]+/([A-Z]-.)].$/\1/g’ -e ‘/^[A-Z].$/p’ git-test.txt

.

Ответ или решение

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

Теория:

Использование sed для обработки строк, извлечённых из терминальных команд таких, как git show-branch, часто встречается в автоматизации текстовой обработки. Здесь основной задачей является извлечение и модификация конкретных частей индивидуальной строки из выходных данных команды. В текущем контексте у нас есть строка вида [username/B-ticketid-description], из которой мы хотим извлечь части ticketid и description, заменив все дефисы в description на пробелы.

sed — это фильтр потокового редактирования (stream editor), который может выполнять различные манипуляции с текстом напрямую на выходе терминала или в файле. Один из его сильных аспектов — это поддержка регулярных выражений, с помощью которых мы можем находить и заменять текстовые шаблоны.

Пример:

Вы уже предоставили пример ввода и желаемого результата:

  • Вход: * [ccushing/B-47120-java-11-take2] B-48141 remove env prefix
  • Ожидаемый выход: B-47120 java 11 take2

Ваша текущая команда sed выглядит так:

git show-branch --current $SHA1 \
  | sed --quiet --regexp-extended \
    's/^.*\* \[[a-z]+\/(B-[0-9]+)-([a-z0-9-]+)\].*/\1 \2/p' \
  | sed --quiet 2p

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

Применение:

Предложенное решение, чтобы этого достичь, будет использовать многошаговую замену и адресацию в sed. Например:

  1. Сначала мы выделяем несколько подходящих строк, содержащих шаблон, с помощью выражения выбора /^[[:blank:]*]+\[[^/]*\/(B-[0-9]+)-([^]]*)\].*/.
  2. Используем захватывающие группы для выбора нужной части строки, потом в контексте этой выборки заменяем все - в ней на пробелы.

Рассмотрите это пошаговое решение:

git show-branch --current $SHA1 | sed -nE '/^[[:blank:]*]+\[[^/]*\/(B-[0-9]+)-([^]]*)\].*/{
             s//\1 \2/; :a s/(B-[0-9]+ [^-]*)-(.*)/\1 \2/;ta; p; }'

Данная команда выполняет следующее:

  • Подбирает строки с требуемым шаблоном.
  • Далее из этих строк извлекает ${1} \2 с помощью s//\1 \2/, где\1 — это ticketid, а \2description.
  • Циклично заменяет - в description на пробелы с использованием метки :a и команды замены s/(B-[0-9]+ [^-]*)-(.*)/\1 \2/;ta.

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

Практический пример:

Продолжая использовать те же принципы, можно адаптировать подход для других сценариев, таких как извлечение различных шаблонов из текстовых данных, которые могут появляться в системных журналах, выходных данных команд UNIX и во многих других областях, где информация представлена в текстовом формате. В вашей ситуации, sed может быть частью скрипта на хук prepare-commit-msg, что позволяет автоматизировать формирование заголовков коммитов, что критично при больших объемах работы в корпоративной среде или в распределённых командах.

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

Оцените материал
Добавить комментарий

Капча загружается...