Как мне сопоставить все, кроме первых совпадений в строке с sed?

Я делаю свои коммит-сообщения в Git с определенным шаблоном, чтобы упростить создание журнала изменений для новых выпусков ( /questions/17268864/vedenie-fajla-changelogtxt-v-dvcs/17268874#17268874).

Каждое изменение, которое должно быть внесено в мой список изменений, имеет префикс CHG, NEW или же FIX,

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

git show --quiet --date=short --pretty=format:"%cd %an %s%n%n%w(100,21,21)%b%n" $CURRENTREVISION

Предмет (%s) содержит предмет модификации.

Затем я использую SED, чтобы изменить сгенерированные результаты так, чтобы они соответствовали потребностям моего файла журнала изменений.

Теперь, случается, что в строке темы, есть несколько случаев CHG, NEW или же FIX, Мой вывод темы выглядит так:

DATE NAME FIX first change NEW second change CHG third change

Я хотел бы поставить префикс для всех ключевых слов, кроме первого, с новой строкой, чтобы каждый CHG, NEW или же FIX начинается новая строка:

DATE NAME FIX first change
          NEW second change
          CHG third change

Что я должен сказать SED, чтобы добиться этого?

4 ответа

Решение

sed не самый подходящий инструмент для этого

С awk это будет выглядеть так

awk '{n=0; for (i=1; i<=NF; i++) {if ($i ~ /(NEW|FIX|CHG)/) {$i=(n++?"\n          ":"")$i}}}7'
  • n=0 (пере) установить флаг
  • for (i=1; i<=NF; i++) цикл по каждому полю линии
  • if ($i ~ /(NEW|FIX|CHG)/) если поле является одним из маркеров
    • $i=(n++?"\n ":"")$i обновить поле, добавив соответствующий начальный пробел (или ни одного)
  • 7 истина-й шаблон для распечатки текущей строки.
awk '{while(++i<=NF){if($i~/FIX|NEW|CHG/){if(f){$i="\n"$i}else{f=1}}}}1'

или даже меньше:

awk '{while(++i<=NF){if($i~/FIX|NEW|CHG/){if(f++){$i="\n"$i}}}}1'

Пример:

$echo "DATE CH NAME FIX first change NEW second change CHG third change" | awk '{while(++i<=NF){if($i~/FIX|NEW|CHG/){if(f){$i="\n"$i}else{f=1}}}}1'

DATE CH NAME FIX first change 
NEW second change 
CHG third change

Идти от 1st to last поля. для любого поля, соответствующего одному из трех шаблонов, мы проверяем, f=1, который будет ложным в случае первого матча. так как мы делаем f++, для следующих матчей это будет верно и, следовательно, "\n" будут добавлены раньше.

sed '/^DATE NAME/ {
:cycle
   s/\(.\{1,\}\) \(FIX .*\)/\1\
\2/g
   t cycle
   s/\(.\{1,\}\) \(NEW .*\)/\1\
\2/g
   t cycle
   s/\(.\{1,\}\) \(CHG .*\)/\1\
\2/g
   t cycle

   s/\n/&          /g
   s/\n */ /
   }' YourFile

что-то подобное для версии posix (--posix на GNU sed).

просто

   s/\(.\{1,\}\) \(\(CHG|FIX|NEW\) .*\)/\1\
\2/g
   t cycle

может заменить 3 первых s/// с GNU sed, что позволяет |

Я немного закрепился с первым /^DATA NAME/ в качестве фильтра, но если обрабатывается только этот вид линии, в этом нет необходимости (и {})

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

вместо этого я думаю, может быть Perl это фантастический инструмент для этого.

что-то вроде этого:

while(<STDIN>){
    my @matches = m/^(.*?)((?:FIX|NEW|CHG).*?)*$/;
    my $date_name = unshift @matches; # only FIX, NEW, CHG remains now
    print $date_name, unshift @matches;
    while (@matches) { print "\t\t", unshift @matches; }
}

передать ваши исходные данные и перенаправить в файл в оболочке.

Другие вопросы по тегам