為什麼這個 sed 指令不替換倒數第三個「and」?

為什麼這個 sed 指令不替換倒數第三個「and」?

更新 2020 年 5 月 26 日

看來這是一個錯誤,所以我提交了一個錯誤。它的 ID 是#41558。


我只是在亂搞sed,我想出了這個練習:替換倒數第三個出現的“and”(這個詞,不是子字串),創建:

dog XYZ foo and bar and baz land good

我以為這會起作用

echo 'dog and foo and bar and baz land good' |
    sed -E 's/(.*)\band\b((.*\band\b){2})/\1XYZ\2/'

但它實際上取代了倒數第二個出現的“and”。我能想到的唯一解釋是它包含“土地”作為其中之一\band\b,但情況不應該是這樣,因為我包含了“\b邊界”一詞?

答案1

這很難做到,因為sed不支援環視等(正如您可以在 PCRE 中所做的那樣)。反轉字串並替換從頭開始第三次出現的反轉單詞,然後再次反轉會更容易。

$ echo 'dog and foo and bar and baz land good' | rev | sed 's/\<dna\>/XXX/3' | rev
dog XXX foo and bar and baz land good

至於為什麼你的表達不起作用,這看起來像是個錯誤。反向引用\3似乎是 string  baz land,就好像\bbefore andin.*\band\b從未產生過任何效果。

命令

sed -E 's/(.*)\<and\>((.*\<and\>){2})/\1XYZ\2/'

sed似乎在 OpenBSD 上用它的原生(使用\<\>代替\b)做了正確的事情。

sed我還沒有找到針對 GNU或 GNU 的現有錯誤報告glibc,儘管如果它至少是這樣的話我不會感到驚訝有關的glibc 錯誤 25322(因為,見下文)。

您可以透過更詳細的方式來解決這個問題:

sed -E 's/(.*)\band\b(.*\band\b.*\band\b)/\1XYZ\2/'

答案2

我建議提出問題。我已經測試了這些範例,這會導致GNU grep,GNU sed和產生相同的行為GNU awk。除了下面提到的一種情況。

  • 錯誤的輸出:

    $ echo 'cocoa' | sed -nE '/(\bco){2}/p'
    cocoa
    

    sed -nE '/(\<co){2}/p'並且awk '/(\<co){2}/'也有錯誤的行為,但grep -E '(\<co){2}'正確地沒有給出輸出

  • 行為正確,沒有輸出:

    $ echo 'cocoa' | sed -nE '/\bco\bco/p'
    
  • it輸出錯誤:後面只有 1 個完整單字with

    $ echo 'it line with it here sit too' | sed -E 's/with(.*\bit\b){2}/XYZ/'
    it line XYZ too
    
  • 行為正確,輸入未修改

    $ echo 'it line with it here sit too' | sed -E 's/with.*\bit\b.*\bit\b/XYZ/'
    it line with it here sit too
    
  • 將單字邊界更改為\<\>會導致不同的問題。

    這正確不修改輸入:

    $ echo 'it line with it here sit too' | sed -E 's/with(.*\<it\>){2}/XYZ/'
    it line with it here sit too
    

    這正確修改了輸入

    $ echo 'it line with it here it too' | sed -E 's/with(.*\<it\>){2}/XYZ/'
    it line XYZ too
    

    但是這個無法修改輸入

    $ echo 'it line with it here it too sit' | sed -E 's/with(.*\<it\>){2}/XYZ/'
    it line with it here it too sit
    

此外,只有當衝突的單字開頭有額外的字元時,才會出現有問題的行為。例如,itsit。但如果末尾有字元則不然。例如,itsiteitem

$ echo 'it line with it here item too' | sed -E 's/with(.*\bit\b){2}/XYZ/'
it line with it here item too
$ echo 'it line with it here it too item' | sed -E 's/with(.*\<it\>){2}/XYZ/'
it line XYZ too item

相關內容