簡體   English   中英

僅替換最后一條匹配行(perl單行)

[英]Substitution only on last matching line (perl one-liner)

我有以下形式的多個文件

version 'aaa'
other 'bbb'
another 'ccc'
version 'ddd'
onemore 'eee'

有些具有一個version ,另一些具有多個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單一代碼只對最近出現的匹配采取行動嗎?

您可能會想使用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 )。

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM