【问题标题】:Substitution only on last matching line (perl one-liner)仅在最后一个匹配行上替换(perl 单行)
【发布时间】:2015-03-24 11:54:39
【问题描述】:

我有多个表单文件

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 单行代码仅对最近发生的匹配进行操作?

【问题讨论】:

  • 你的文件有多大?
  • 不大。至少 11 行,但不应超过 90 行。
  • 取决于你的线条有多宽,这是一个文件

标签: macos bash perl


【解决方案1】:

我建议您使用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"

【讨论】:

    【解决方案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)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-03-22
      • 2013-12-26
      • 2012-09-19
      • 1970-01-01
      • 2018-04-08
      相关资源
      最近更新 更多