2

Как мне манипулировать соответствием последней строки в тексте? У меня есть файл конфигурации, в котором я хочу изменить определенное значение:

val1=0
val2=a
val3=?
val2=lol

Я только хочу изменить последнюю строку, соответствующую значению grep for. sed -i 's/\(^val2=\)/\1yes/' - хорошее начало, но заменяет обе строки val2 вместо только последней.

Я очень широко обозначил вопрос как Unix, потому что я открыт для любого решения: sed или awk, Perl или Ruby, Bash или что-то еще. Более короткое решение сексуальнее, чем глоток.

3 ответа3

1

На основе сценария, найденного здесь:

sed -r ':0;/^val2=/!b;:1;$!N;/\n.*word/{h;s/\n[^\n]*$//p;g;s/^.*\n//};$!b 1;s/^(.*)(val2=)[^\n]*/\1\2yes/' file
1

Лучшее, что я придумал, это:

tac config |while read LINE; do
  if [ "$DONE" != 1 ] && echo "$LINE" |grep '^\s*val2\s*=' >/dev/null; then
    echo "$LINE" |sed 's/^\(\s*val2\s*=\s*\)/\1yes/'
    DONE=1
  else
    echo "$LINE"
  fi
done |tac >config.new

Но это не похоже на быстрые и грязные конвейеры, которые мне нравятся для такой общей задачи.

1

Общая идея: читать входные данные в виде блоков, разделенных \nval2= . Печатайте каждый блок как есть, кроме последнего, где мы меняем текст до первого символа новой строки. Вот относительно простое решение gawk, которое обрабатывает большинство случаев:

gawk -v 'RS=\nval2=' -v ORS= \
  'RT {print $0 RT} !RT {sub(/[^\n]*/, "yes"); print $0}'

Это предполагает, что первая строка не начинается с val2= и что есть хотя бы одно совпадение. Исправление обоих дефектов значительно усложняет скрипт:

gawk -v 'RS=\nval2=' -v ORS= \
  '!RT {if (NR==1) sub(/^val2=[^\n]*/, "val2=yes"); else sub(/[^\n]*/, "yes")}
   {print $0 RT}'

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

perl -e 'undef $/;
         @chunks = split /(?=^val2=)/m, <>;
         $chunks[@chunks-1] =~ s/(?<=^val2=).*/yes/ if @chunks;
         print @chunks'

С помощью расширенных регулярных выражений Perl мы можем использовать одну подстановку регулярных выражений. Просто сопоставьте ^val2= , за которым не следует другой ^val2=:

perl -e 'undef $/; $_=<>; s/^val2=.*\n(?!.*\nval2=)/val2=yes\n/m; print'

Я попытался воспользоваться преимуществами rsplit Python, который может отделить последние N полей, но сдался, когда не смог заставить его работать, не будучи намного более загадочным, чем мои Perl-решения.

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .