Sed

在兩個匹配行之後替換匹配文本

  • November 10, 2015

我有一個 YAML 文件,其中包含一個節,如下所示:

admin::common::passwords:
 alice:
   password: '$6$oTQhLvN/4VFJPscD$8LYwUMSFi'
 bob:
   password: '$6$JKOtLF0wHeZfIskt$W/M5.ugDS'

如果 shell 變數 $ ACCOUNT contains the account name (alice, bob, etc), and $ YAML 是 YAML 文件的文件名,我可以用 sed 添加一個新條目,像這樣。它將新帳戶直接插入到“admin::common::passwords:”行下,所以我知道它在正確的節中:

 sed -i /'^admin::common::passwords:'/a"\  ${ACCOUNT}:\n    password: '${ENCRYPTED_PASS}'" $YAML

我可以使用 awk 找到這樣的現有帳戶。同樣,這會找到該節,然後在該節中找到帳戶(這可能都可以在 awk 中完成,但我在 awk 方面還不夠好,這將完成這項工作):

 awk "BEGIN{RS=ORS="\n\n";FS=OFS="\n"}/admin::common::passwords:/" $YAML | grep -A1 "^[ ]*${ACCOUNT}:"

但是要刪除一行,我能想到的最好的方法是:

sed -i "/^  ${ACCOUNT}:/,+1d" $YAML

這將刪除文件中任何位置的任何行“^ alice:”,無論它是在 admin::common::passwords 節還是其他地方。

我不能對文件的內容做任何假設,除了它確實包含一個 admin::common:passwords 節(如果沒有,那麼它將由我的腳本的另一部分創建;那一點是簡單的)。

所以,我的問題是:我怎樣才能改進這個 sed 說“在 admin::common::passwords 節中搜尋“^ ${ACCOUNT}:” ,並刪除匹配的行以及它下面的行’ ?

(當然,它不必是 sed;可能是 awk 甚至 Perl 更適合這項任務)

sed "
 /^admin::common::passwords:$/,/^[^ ].*:$/ {
    /^  $ACCOUNT:$/ {
      N;d
    }
 }" < "$YAML"

也就是說,將您的刪除節包含在admin::common...與 next之間匹配的部分中whatever-section:

請注意,.使用者名中常見的字元也是正則表達式運算符,因此john.doe可以匹配john.doe但也可以匹配johnWdoe

admin::common::passwords請注意,如果您有兩個連續的部分並且要刪除的帳戶在第二個中,則上述方法將不起作用。

如果,正如 Otheus 所指出的那樣,部分執行到具有相同或更少前導空格字元的下一行,並且您的admin::common::passwords:部分可能有一些前導空格,那麼可能是時候切換到另一種語言,例如awk

awk -v account="$ACCOUNT" '
 match($0, /[^ ]/) && RSTART <= n {n = 0}
 n && NF == 1 && $1 == account ":" {getline; next}
 !n && /^ *admin::common::passwords:$/ {
   match($0, /[^ ]/)
   n = RSTART
 }
 {print}' < "$YAML"

引用自:https://unix.stackexchange.com/questions/242110