操纵最后一行匹配

操纵最后一行匹配

如何操作文本中的最后一行匹配?我有一个配置文件,我想更改其中的某个值:

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

我只想将与值匹配的最后一行更改为 grep。sed -i 's/\(^val2=\)/\1yes/'是一个好的开始,但要替换两val2行而不是只替换最后一行。

我把这个问题标记为 Unix,因为我愿意接受任何解决方案:sed 或 awk、Perl 或 Ruby、Bash 或其他。简短的解决方案比拗口的解决方案更有吸引力。

答案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

但对于如此常见的任务来说,这并不像我喜欢的那种快速而粗糙的管道。

答案2

根据找到的脚本这里

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

答案3

总体思路:以 分隔的块形式读取输入\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'

我尝试利用 Pythonrsplit来分离最后 N 个字段,但是当我发现它比我的 Perl 解决方案更难懂时我就放弃了。

相关内容