【问题标题】:replacing multi-line string with sed用 sed 替换多行字符串
【发布时间】:2019-06-18 19:20:01
【问题描述】:

我在日志中的内容。

    ---POLICIES WITH 172.25.22.16 AS SOURCE ADDRESS---
    ---POLICIES WITH 172.25.22.16 AS DESTINATION ADDRESS---
    ---POLICIES WITH 172.25.22.17 AS SOURCE ADDRESS---
    some output
    ---POLICIES WITH 172.25.22.17 AS DESTINATION ADDRESS---
    ---POLICIES WITH 172.25.22.18 AS SOURCE ADDRESS---
    some output
    ---POLICIES WITH 172.25.22.18 AS DESTINATION ADDRESS---
    ---POLICIES WITH 172.25.22.19 AS SOURCE ADDRESS---

想要什么

    ---POLICIES WITH 172.25.22.16 AS SOURCE ADDRESS---
    None
    ---POLICIES WITH 172.25.22.16 AS DESTINATION ADDRESS---
    None
    ---POLICIES WITH 172.25.22.17 AS SOURCE ADDRESS---
    some output
    ---POLICIES WITH 172.25.22.17 AS DESTINATION ADDRESS---
    None
    ---POLICIES WITH 172.25.22.18 AS SOURCE ADDRESS---
    some output
    ---POLICIES WITH 172.25.22.18 AS DESTINATION ADDRESS---
    None
    ---POLICIES WITH 172.25.22.19 AS SOURCE ADDRESS---
    None

我试过 sed 's:---\n---:----\nNone\n---/:g' 没有这样做。 我需要对所有以 --- 结尾的行进行一些替换,仅当下一行以 --- 开头时

【问题讨论】:

  • 默认情况下,Sed 不能匹配跨行的模式。它一次只读取一个以换行符结尾的输入行。你可以让它读得更多,但到那时你就到了 sed 的 weird 部分。
  • 行是否以--- 开头或--- 前面是否有空格,如果您的示例中有空格,下面的大多数答案都不起作用。

标签: bash awk sed


【解决方案1】:

我会写

awk '
  /^---/ && prev ~ /^---/ {print "None"}
  {print; prev = $0}
  END {if (/^---/) print "None"}
' file

一点烘干机

awk '
  function none() {if ($0 ~ /^---/ && prev ~ /^---/) {print "None"}}
  {none(); print; prev = $0}
  END {none()}
' file

【讨论】:

  • 我喜欢这个主意,但你忘了最后一行 :)
  • 您确实忘记了该行应该以---结尾
  • 嗯,这是对正则表达式的轻微调整,应该很明显。
  • 您需要在 END 部分测试 if (prev ~ /^---/) 的可移植性,而不是 if (/^---/if ($0 ~ /^---/,因为 END 中 $0 的值未由 POSIX 定义,因此会有不同不同 awks 中的值(可能是读取的最后一条记录或 null)。鉴于此,我不知道如何在第二个脚本中写 none()
【解决方案2】:

您可以使用 GNU sed(用于 -E-z 并将 \n 识别为换行符)使用:

$ sed -Ez 's/(---\n)(---|$)/\1None\n\2/g' file
---POLICIES WITH 172.25.22.16 AS SOURCE ADDRESS---
None
---POLICIES WITH 172.25.22.16 AS DESTINATION ADDRESS---
None
---POLICIES WITH 172.25.22.17 AS SOURCE ADDRESS---
some output
---POLICIES WITH 172.25.22.17 AS DESTINATION ADDRESS---
None
---POLICIES WITH 172.25.22.18 AS SOURCE ADDRESS---
some output
---POLICIES WITH 172.25.22.18 AS DESTINATION ADDRESS---
None
---POLICIES WITH 172.25.22.19 AS SOURCE ADDRESS---
None

或 GNU awk 用于多字符 RS 和 gensub():

$ awk -v RS='^$' -v ORS= '{$0=gensub(/(---\n)(---|$)/,"\\1None\n\\2","g")}1' file
---POLICIES WITH 172.25.22.16 AS SOURCE ADDRESS---
None
---POLICIES WITH 172.25.22.16 AS DESTINATION ADDRESS---
None
---POLICIES WITH 172.25.22.17 AS SOURCE ADDRESS---
some output
---POLICIES WITH 172.25.22.17 AS DESTINATION ADDRESS---
None
---POLICIES WITH 172.25.22.18 AS SOURCE ADDRESS---
some output
---POLICIES WITH 172.25.22.18 AS DESTINATION ADDRESS---
None
---POLICIES WITH 172.25.22.19 AS SOURCE ADDRESS---
None

但我强烈建议您使用以下 POSIX awk 来代替,以实现可移植性,而不是一次将整个文件读入内存,并且如果您需要进行任何更改,也可以简单地增强它:

$ cat tst.awk
/^---/ { if (NR>1) prt(); hdr=$0; next }
{ txt = txt ORS $0 }
END { prt() }
function prt() {
    print hdr (txt=="" ? ORS "None" : txt)
    hdr = txt = ""
}

$ awk -f tst.awk file
---POLICIES WITH 172.25.22.16 AS SOURCE ADDRESS---
None
---POLICIES WITH 172.25.22.16 AS DESTINATION ADDRESS---
None
---POLICIES WITH 172.25.22.17 AS SOURCE ADDRESS---
some output
---POLICIES WITH 172.25.22.17 AS DESTINATION ADDRESS---
None
---POLICIES WITH 172.25.22.18 AS SOURCE ADDRESS---
some output
---POLICIES WITH 172.25.22.18 AS DESTINATION ADDRESS---
None
---POLICIES WITH 172.25.22.19 AS SOURCE ADDRESS---
None

【讨论】:

  • 此测试是否如 OP 请求中的最后三个字符为 ---
  • 不只是前 3 个,他是否也需要以 --- 结尾并不明显,但如果是这样,将 /^---/ 更改为 /^---.*---$/ 显然是微不足道的。
  • 这似乎行得通。来自 OP 的请求:“我需要对所有以 --- 结尾的行进行一些替换,仅当下一行以 --- 开头时”
  • 是的,但我认为这是因为 OP 试图提出一个 sed 解决方案,而这种方法很明显,我认为它在功能上不是必需的。不过,OP 总是可以纠正我们。
【解决方案3】:

这可能对你有用(GNU sed):

sed -e ':a;/^---.*---$/{${aNone' -e 'b};n;//!b;iNone' -e 'ba}' file

如果当前行以--- 开始和结束,并且它是文件的最后一行,追加字符串None。否则,打印当前行并获取下一行,如果该行也以 --- 开头和结尾,插入 None 并重复。

【讨论】:

    【解决方案4】:

    使用 awk

    awk '{
            if($0 ~ /^-{3}/){
                if(header==1){
                    print "None"
                }; 
                header = 1
            }else{
                header = 0
            }
         }1
         END{
            if(header){
                print "None"
            }
         }' <input>
    

    更短:

    awk '!/^-{3}/{header=0;print;next}header{print "None"}{header=1}1;END{if(header){print "None"}}' <input>
    

    【讨论】:

    • 这不接受以---结尾并以---开头的请求。即使最后的--- 丢失,它也会提供输出
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-28
    • 1970-01-01
    • 1970-01-01
    • 2020-03-13
    • 1970-01-01
    • 2021-06-21
    相关资源
    最近更新 更多