Как мне сопоставить все, кроме первых совпадений в строке с 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; }
}
передать ваши исходные данные и перенаправить в файл в оболочке.