Управляйте последним соответствием строки

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

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

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

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

2
задан 10.09.2010, 17:18

3 ответа

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

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
ответ дан 08.12.2019, 07:30

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

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
ответ дан 08.12.2019, 07:30

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

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 расширил regexps, мы можем использовать единственную regexp замену. Просто соответствуйте ^val2= это не сопровождается другим ^val2=:

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

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

1
ответ дан 08.12.2019, 07:30

Теги

Похожие вопросы