如何操作文本中的最后一行匹配?我有一个配置文件,我想更改其中的某个值:
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 解决方案更难懂时我就放弃了。