簡體   English   中英

sed:如果匹配模式,則打印分隔的行塊

[英]sed: print delimited block of lines if it matches a pattern

我想用sed以匹配模式1 / PATTERN2分隔的行的塊,然后執行操作僅在包含pattern3塊(例如打印塊)。

在下面的示例中,我正在尋找“ 抓住我,如果可以的話 ”,在由匹配{}的行分隔的所有塊中(然后我想要完整地打印匹配的塊)。

我嘗試過的:

sed -n -e '/{/,/}/{1h;1!{$!{H;d};H;x;/catch me if you can/p}}'

(想法是匹配由{}分隔的塊,然后將每個塊累積到保留空間;在每個塊的末尾,交換保持空間並執行匹配以“ 抓住我,如果可以的話 ”)。 這不起作用,因為所有匹配的塊一起被sed視為單個塊,而不是單獨處理每個塊。

輸入數據

"block1": {
    "foo": "abcd",
    "bar": "catch me if you can",
    "aaa": "12345"
},
"block2": {
    "bbb": "24680",
    "bar": "blah",
    "foo": "argh",
    "ccc": "135"
},
"block3": {
    "ddd": "zzz"
},
"block4": {
    "foo": "xyz",
    "bar": "catch me if you can",
}

期望的輸出

"block1": {
    "foo": "abcd",
    "bar": "catch me if you can",
    "aaa": "12345"
},
"block4": {
    "foo": "xyz",
    "bar": "catch me if you can"
},

注1:每個塊內的字段順序是隨機的。 字段數和值的長度在塊之間不是恆定的。 我正在尋找的字段可能在某些塊中丟失(而不是僅具有不同的值)。

注2:出於教育目的, 我更喜歡使用sed的解決方案 ,但如果不可能,awk或bash也可以。 請不要使用perl或其他工具。

參考文獻:

  1. Sed命令摘要
  2. Sed一個襯里

我就是這樣做的。 這里有兩個版本,一個用於BSD(Mac OS X) sed (也適用於其他未運行GNU sed系統),另一個用於GNU sed

BSD sed

$ cat script.bsd-sed
/{/,/}/{
    /{/{ h; b next
    }
    /}/{ H; x; /catch me if you can/p; b next
    }
    H
    :next
}
$ sed -n -f script.bsd-sed data
"block1": {
    "foo": "abcd",
    "bar": "catch me if you can",
    "aaa": "12345"
},
"block4": {
    "foo": "xyz",
    "bar": "catch me if you can",
}
$

邏輯是:

  • 除非被告知這樣做( -n ),否則不要打印任何內容。
  • 在包含{}行之間
  • 如果該行匹配{ ,則將該模式復制到保留空間並跳轉到next標簽。
  • 如果該行匹配} ,則將其添加到保留空間; 切換模式並保持空間; 如果圖案空間(先前保持空間)與您的其他圖案匹配(如果可以,請抓住我),打印它; 跳轉到標記next
  • 將行添加到保留空間。

BSD(經典) sedb next之后的行上不需要任何內容​​,因此動作的}在下一行。

GNU sed

$ cat script.gnu-sed 
/{/,/}/{
    /{/{ h; b next }
    /}/{ H; x; /catch me if you can/p; b next }
    H
    :next
}
$ /opt/gnu/bin/sed -n -f script.gnu-sed data
"block1": {
    "foo": "abcd",
    "bar": "catch me if you can",
    "aaa": "12345"
},
"block4": {
    "foo": "xyz",
    "bar": "catch me if you can",
}
$

GNU sed在標簽終止命令后識別分號或閉括號,因此它允許更緊湊的表示法。 你甚至可以將它們整合成一行 - 你必須添加幾個分號:

$ /opt/gnu/bin/sed -n -e '/{/,/}/{ /{/{ h; b next }; /}/{ H; x; /catch me if you can/p; b next }; H; :next }' data
"block1": {
    "foo": "abcd",
    "bar": "catch me if you can",
    "aaa": "12345"
},
"block4": {
    "foo": "xyz",
    "bar": "catch me if you can",
}
$

您也可以刪除不在模式匹配中的空格:

$ /opt/gnu/bin/sed -n -e '/{/,/}/{/{/{ h;b next};/}/{H;x;/catch me if you can/p;b next};H;:next}' data
"block1": {
    "foo": "abcd",
    "bar": "catch me if you can",
    "aaa": "12345"
},
"block4": {
    "foo": "xyz",
    "bar": "catch me if you can",
}
$

擴展數據文件data

"block1": {
    "foo": "abcd",
    "bar": "catch me if you can",
    "aaa": "12345"
},
"block2": {
    "bbb": "24680",
    "bar": "blah",
    "foo": "argh",
    "ccc": "135"
},
"block3": {
    "ddd": "zzz"
},
"block4": {
    "foo": "xyz",
    "bar": "catch me if you can",
}
"block5": [
    "oops": "catch me if you can"
],
"block6": {
    "rhubarb": "dandelion"
}

使用sed

$ sed -n '/^"/{x;/catch/p;d}; ${H;x;/catch/p;d}; H' file
"block1": {
    "foo": "abcd",
    "bar": "catch me if you can",
    "aaa": "12345"
},
"block4": {
    "foo": "xyz",
    "bar": "catch me if you can",
}

這個怎么運作

  • -n

    除非我們要求,否則此選項告訴sed不要打印任何內容

  • /^"/{x;/catch/p;d}

    對於以引號開頭的任何行,這(1)交換模式並保持空間,(2)檢查模式空間中現在的內容是否已catch ,如果是,則打印它,以及(3)刪除模式空間,sed開始在下一行上工作。

  • ${H;x;/catch/p;d}

    當我們到達最后一行時,我們會做類似的事情。 我們將最后一行添加到保留空間,將保留空間交換到模式空間,檢查它是否包含catch ,如果是,則打印它。 然后刪除模式空間。

  • H

    對於任何其他情況,該行將附加到保留空間。

使用awk

$ awk '/catch/{print $0 "},"}' RS='}' file
"block1": {
    "foo": "abcd",
    "bar": "catch me if you can",
    "aaa": "12345"
},
,
"block4": {
    "foo": "xyz",
    "bar": "catch me if you can",
},

改進

Jonathan Leffler增加了方括號塊的可能性以及大括號塊,如他的測試文件data 在sed的情況下,嘗試:

$ sed -n '/^"/{x;/{.*catch/p;d}; ${H;x;/{.*catch/p;d}; H' data
"block1": {
    "foo": "abcd",
    "bar": "catch me if you can",
    "aaa": "12345"
},
"block4": {
    "foo": "xyz",
    "bar": "catch me if you can",
}

而對於awk:

$ awk '{s=(s?s"\n":"") $0} /{/{f=1} f && /catch/{f=2} /^[]}]/{if (f==2) print s; f=0; s=""} ' data
"block1": {
    "foo": "abcd",
    "bar": "catch me if you can",
    "aaa": "12345"
},
"block4": {
    "foo": "xyz",
    "bar": "catch me if you can",
}

sed用於單個行上的簡單替換, 即全部 在40年前發明awk時,所有比s,g和p(帶-n)更多的結構都變得過時了。

使用GNU awk進行多字符RS和RT:

$ awk -v RS='},?\n' -v ORS= '/catch me if you can/{print $0 RT}' file
"block1": {
    "foo": "abcd",
    "bar": "catch me if you can",
    "aaa": "12345"
},
"block4": {
    "foo": "xyz",
    "bar": "catch me if you can",
}

暫無
暫無

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

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