我有多个表格
的文件version 'aaa'
other 'bbb'
another 'ccc'
version 'ddd'
onemore 'eee'
有些人有一个version
,有些人有多个;与其他键相同,但值不会重复。作为更大的bash
函数的一部分,我正在使用perl单行来修改值
modify_value() {
key_to_modify="$1"
new_value="$2"
perl -i'' -pe "s|^(\s*)${key_to_modify} .*|\1${key_to_modify} ${new_value}|" "${file}"
}
线条上的缩进变化且不可预测,但应该得到尊重(因此需要^(\s*)
)。此功能在一定程度上起作用。我能做到
modify_value "onemore" "fff"
它将在文本文件中正确替换。但是,它崩溃的地方是我有多个具有相同名称的键(例如前面提到的version
),因为这些更改将在所有这些中进行。在我的特定情况下,我希望在最后一种情况下总是进行修改。
由于价值从不重复,到目前为止我所拥有的是
modify_value() {
key_to_modify="$1"
new_value="$2"
last_key=$(cat "${file}" | grep "^\s*${key_to_modify}" | tail -1 | perl -pe 's/^\s*//')
perl -i'' -pe "s|^(\s*)${last_key}|\1${key_to_modify} ${new_value}|" "${file}"
}
这有效,但有点不优雅。是否有可能利用perl one-liner仅对匹配的最新事件采取行动呢?
答案 0 :(得分:2)
你可能想要使用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/; }
' "$1" "$2" "$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);
' "$1" "$2" "$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;
' "$1" "$2" "$file"
或
perl -0777 -i -e'
$key = shift(@ARGV);
$val = shift(@ARGV);
$_ = <>;
s/\A.*^\s*\Q$key\E\s\K[^\n]*/$val/sm;
print;
' "$1" "$2" "$file"
或
perl -0777 -i -pe'
BEGIN {
$key = shift(@ARGV);
$val = shift(@ARGV);
}
s/\A.*^\s*\Q$key\E\s\K[^\n]*/$val/sm;
' "$1" "$2" "$file"
如果内存有问题,请使用File :: ReadBackwards(或类似的高效工具)反转输入,更改第一个匹配,然后使用File :: ReadBackwards反转输出。
这些解决方案还修复了$key_to_modify
和$new_value
对Perl程序的不正确插值(通过将值作为args传递)。
这些解决方案还修复了$key_to_modify
对正则表达式的不正确插值(使用\Q
)。
答案 1 :(得分:0)
我建议您使用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"