Macos 仅在最后一个匹配行上进行替换(perl一行)

Macos 仅在最后一个匹配行上进行替换(perl一行),macos,bash,perl,Macos,Bash,Perl,我有多个表格文件 version 'aaa' other 'bbb' another 'ccc' version 'ddd' onemore 'eee' 有的有一个版本,有的有多个;与其他键相同,但值从不重复。作为一个更大的bash函数的一部分,我使用perl one liner来修改值 modify_value() { key_to_modify="$1" new_value="$2" perl -i'' -pe "s|^(\s*)${key_to_modify} .*|\1$

我有多个表格文件

version 'aaa'
other 'bbb'
another 'ccc'
version 'ddd'
onemore 'eee'
有的有一个
版本
,有的有多个;与其他键相同,但值从不重复。作为一个更大的
bash
函数的一部分,我使用perl one liner来修改值

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"
它将在文本文件中正确替换。但是,出现故障的地方是我有多个同名键(如前面提到的
版本
),因为所有键都会发生此更改。在我的特殊情况下,我希望在最后一种情况下总是进行修改

由于价值观从未重复过,到目前为止,我所拥有的是

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仅对最新出现的匹配执行操作?

我建议您使用,它允许您以行数组的形式访问文件。对数组所做的任何修改都会反映在文件中。自从Perl5的版本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/; }
' "$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"
如果内存有问题,请使用File::ReadBackwards(或类似的高效工具)反转输入,更改第一个匹配项,然后使用File::ReadBackwards反转输出


这些解决方案还修复了将
$key_to_modify
$new_value
插入Perl程序的错误(通过将值作为args传递)


这些解决方案还修复了将
$key\u修改
到正则表达式中的错误插值(使用
\Q
)。

文件有多大?不是很大。至少11行,但不应超过90行。根据行的宽度,这是一个小文件
# 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"