仅在最后匹配的行上替换(perl 单行)
Substitution only on last matching line (perl one-liner)
我有多个格式的文件
version 'aaa'
other 'bbb'
another 'ccc'
version 'ddd'
onemore 'eee'
有的有一个version
,有的有多个;与其他键相同,但值从不重复。作为更大的 bash
函数的一部分,我正在使用一个 perl 单行代码来修改值
modify_value() {
key_to_modify=""
new_value=""
perl -i'' -pe "s|^(\s*)${key_to_modify} .*|${key_to_modify} ${new_value}|" "${file}"
}
行中的缩进各不相同且不可预测,但应予以尊重(因此需要 ^(\s*)
)。这个功能在一定程度上很管用。我可以
modify_value "onemore" "fff"
并且会在文本文件中正确替换。但是,它崩溃的地方是我有多个同名键(例如前面提到的 version
),因为将在所有键中进行此更改。在我的特殊情况下,我希望在最后一种情况下始终进行修改。
由于值永远不会重复,所以到目前为止我所拥有的是
modify_value() {
key_to_modify=""
new_value=""
last_key=$(cat "${file}" | grep "^\s*${key_to_modify}" | tail -1 | perl -pe 's/^\s*//')
perl -i'' -pe "s|^(\s*)${last_key}|${key_to_modify} ${new_value}|" "${file}"
}
这可行,但有点不雅观。是否可以利用 perl 单行代码仅对最近出现的匹配项采取行动?
我建议您使用 Tie::File
,它允许您以行数组的形式访问文件。对数组所做的任何修改都会反映在文件中。自 Perl 5 版本 8 以来,它一直是核心模块,因此应该不需要安装。
这种单行代码的工作方式是从头到尾检查文件的每一行,并在找到匹配项后立即停止。看起来还可以,但我目前无法测试它。
perl -MTie::File -e"tie @f,'Tie::File',\"${file}\"; s/^\s*${key_to_modify}\s\K.*/${new_value}/ and last for reverse @f"
您可能会想使用 Tie::File。
# Borodin's solution with the bug fixes I mention below.
perl -MTie::File -e'
$key = shift(@ARGV);
$val = shift(@ARGV);
$file = shift(@ARGV);
tie @f, "Tie::File", $file;
for (reverse @f) { last if s/^\s*\Q$key\E\s\K.*/$val/; }
' "" "" "$file"
对于小文件,Tie::File 将提供比替代方案更慢并且比替代方案使用更多内存的解决方案
对于大文件,Tie::File 将提供一个极其缓慢的解决方案来解决这个问题,尽管它使用的内存比将整个文件加载到内存中要少。
对于这个问题,你真的不能比使用 Tie::File 更糟糕了。
这是一个替代方案:
perl -i -e'
$key = shift(@ARGV);
$val = shift(@ARGV);
my @f = reverse(<>);
for (@f) { last if s/^\s*\Q$key\E\s\K.*/$val/; }
print reverse(@f);
' "" "" "$file"
您甚至可以通过让替换运算符找到最后一个匹配项来避免双重反转。
# 5.14+
perl -0777 -i -e'
$key = shift(@ARGV);
$val = shift(@ARGV);
print <> =~ s/\A.*^\s*\Q$key\E\s\K[^\n]*/$val/smr;
' "" "" "$file"
或
perl -0777 -i -e'
$key = shift(@ARGV);
$val = shift(@ARGV);
$_ = <>;
s/\A.*^\s*\Q$key\E\s\K[^\n]*/$val/sm;
print;
' "" "" "$file"
或
perl -0777 -i -pe'
BEGIN {
$key = shift(@ARGV);
$val = shift(@ARGV);
}
s/\A.*^\s*\Q$key\E\s\K[^\n]*/$val/sm;
' "" "" "$file"
如果内存有问题,请使用 File::ReadBackwards(或类似的高效工具)反转输入,更改第一个匹配项,然后使用 File::ReadBackwards 反转输出。
这些解决方案还修复了 $key_to_modify
和 $new_value
到 Perl 程序中的不正确插值(通过将值作为参数传递)。
这些解决方案还修复了 $key_to_modify
到正则表达式中的不正确插值(通过使用 \Q
)。
我有多个格式的文件
version 'aaa'
other 'bbb'
another 'ccc'
version 'ddd'
onemore 'eee'
有的有一个version
,有的有多个;与其他键相同,但值从不重复。作为更大的 bash
函数的一部分,我正在使用一个 perl 单行代码来修改值
modify_value() {
key_to_modify=""
new_value=""
perl -i'' -pe "s|^(\s*)${key_to_modify} .*|${key_to_modify} ${new_value}|" "${file}"
}
行中的缩进各不相同且不可预测,但应予以尊重(因此需要 ^(\s*)
)。这个功能在一定程度上很管用。我可以
modify_value "onemore" "fff"
并且会在文本文件中正确替换。但是,它崩溃的地方是我有多个同名键(例如前面提到的 version
),因为将在所有键中进行此更改。在我的特殊情况下,我希望在最后一种情况下始终进行修改。
由于值永远不会重复,所以到目前为止我所拥有的是
modify_value() {
key_to_modify=""
new_value=""
last_key=$(cat "${file}" | grep "^\s*${key_to_modify}" | tail -1 | perl -pe 's/^\s*//')
perl -i'' -pe "s|^(\s*)${last_key}|${key_to_modify} ${new_value}|" "${file}"
}
这可行,但有点不雅观。是否可以利用 perl 单行代码仅对最近出现的匹配项采取行动?
我建议您使用 Tie::File
,它允许您以行数组的形式访问文件。对数组所做的任何修改都会反映在文件中。自 Perl 5 版本 8 以来,它一直是核心模块,因此应该不需要安装。
这种单行代码的工作方式是从头到尾检查文件的每一行,并在找到匹配项后立即停止。看起来还可以,但我目前无法测试它。
perl -MTie::File -e"tie @f,'Tie::File',\"${file}\"; s/^\s*${key_to_modify}\s\K.*/${new_value}/ and last for reverse @f"
您可能会想使用 Tie::File。
# Borodin's solution with the bug fixes I mention below.
perl -MTie::File -e'
$key = shift(@ARGV);
$val = shift(@ARGV);
$file = shift(@ARGV);
tie @f, "Tie::File", $file;
for (reverse @f) { last if s/^\s*\Q$key\E\s\K.*/$val/; }
' "" "" "$file"
对于小文件,Tie::File 将提供比替代方案更慢并且比替代方案使用更多内存的解决方案
对于大文件,Tie::File 将提供一个极其缓慢的解决方案来解决这个问题,尽管它使用的内存比将整个文件加载到内存中要少。
对于这个问题,你真的不能比使用 Tie::File 更糟糕了。
这是一个替代方案:
perl -i -e'
$key = shift(@ARGV);
$val = shift(@ARGV);
my @f = reverse(<>);
for (@f) { last if s/^\s*\Q$key\E\s\K.*/$val/; }
print reverse(@f);
' "" "" "$file"
您甚至可以通过让替换运算符找到最后一个匹配项来避免双重反转。
# 5.14+
perl -0777 -i -e'
$key = shift(@ARGV);
$val = shift(@ARGV);
print <> =~ s/\A.*^\s*\Q$key\E\s\K[^\n]*/$val/smr;
' "" "" "$file"
或
perl -0777 -i -e'
$key = shift(@ARGV);
$val = shift(@ARGV);
$_ = <>;
s/\A.*^\s*\Q$key\E\s\K[^\n]*/$val/sm;
print;
' "" "" "$file"
或
perl -0777 -i -pe'
BEGIN {
$key = shift(@ARGV);
$val = shift(@ARGV);
}
s/\A.*^\s*\Q$key\E\s\K[^\n]*/$val/sm;
' "" "" "$file"
如果内存有问题,请使用 File::ReadBackwards(或类似的高效工具)反转输入,更改第一个匹配项,然后使用 File::ReadBackwards 反转输出。
这些解决方案还修复了 $key_to_modify
和 $new_value
到 Perl 程序中的不正确插值(通过将值作为参数传递)。
这些解决方案还修复了 $key_to_modify
到正则表达式中的不正确插值(通过使用 \Q
)。