【问题标题】:Replace multiple lines of "!" with one "!"替换多行“!”用一个“!”
【发布时间】:2019-01-04 19:03:54
【问题描述】:

我正在使用 sed 清理 Cisco 交换机中的“show tech”或“configs”。有时在查看配置部分时,会有多行带有“!”分数。我不想全部删除!因为这会改变配置,但我不需要只有一行的大组!标记。

简而言之,而不是:

!interface vlan200
!description 
!
!
!
interface vlan201

我想清理成:

!interface vlan200
!description
!
interface vlan201

我意识到我可以替换/删除 !使用

sed 's/^!//g'

这会删除 !不过从每一行的开头。

想法?

【问题讨论】:

    标签: bash sed replace


    【解决方案1】:
    #!/usr/bin/env bash
    last_line=''
    while IFS= read -r line; do
      [[ $line = '!' && $last_line = '!' ]] && continue
      printf '%s\n' "$line"
      last_line=$line
    done
    

    https://ideone.com/eu7UHm 看到这个运行,输出:

    !interface vlan200
    !description 
    !
    interface vlan201
    

    【讨论】:

    • 感谢您的及时回复。我刚刚开始再次使用脚本,虽然这可能有效,但有一个单行解决方案来实现它是有帮助的。此外,我不知道如何将其应用于我想要操作的文件。不过我敢肯定这是我自己的错!
    • re: 应用这个 -- ./scriptname <infile >outfile 就可以了。或者您可以将这些重定向直接放在done 之后(在同一行)。
    【解决方案2】:

    Perl 的救援:

    perl -ne 'print unless /^!$/ and $previous; $previous = /^!$/' -- file
    
    • -n逐行读取输入
    • 如果当前行包含单个!,则变量$previous 设置为true,在处理下一行时使用它的值来决定是否打印它。

    【讨论】:

    • 谢谢,我也喜欢perl,但是用得还不够。多年后再次开始玩一些 bash shell 脚本......谢谢,这行得通!
    【解决方案3】:

    按照 OP 的要求,使用 sed:

    sed -nr 'p;:loop;$!N;s/^(\!)\n\1$/\1/;tloop;D'
    

    注意:这是 sed 常用单行代码之一的微小调整(仅匹配感叹号),可在此处找到:http://sed.sourceforge.net/sed1line.txt

    【讨论】:

    • 感谢您继续使用 sed.... 但是,perl 解决方案似乎确实可以解决此问题。感谢您提供 sed 链接,我相信这对将来会有所帮助!
    【解决方案4】:

    gregory 列出的 pattern link 的另一个调整 -

    sed '/^!$/N; /^!\n!$/!P; D'
    

    这来自于基本模式

     sed '$!N; /^\(!\)\n\1$/!P; D'
    

    修改自

     # delete duplicate, consecutive lines from a file (emulates "uniq").
     # First line in a set of duplicate lines is kept, rest are deleted.
     sed '$!N; /^\(.*\)\n\1$/!P; D'
    

    解释

    • 伙计们,请检查我。 使用GNU's manual作为参考,很好用,而且这里似乎与其他版本没有冲突。

    没有

    向模式空间添加一个换行符,然后将下一行输入附加到模式空间。如果没有更多输入,则 sed 退出,不再处理任何命令。

    附加 !地址规范末尾的字符(在命令字母之前)否定了匹配的意义。也就是说,如果 !字符跟在地址或地址范围之后,则只会选择与地址不匹配的行。

    $ 是文件的结尾,所以$!N 表示在不是文件末尾最后一行的行上,“在模式空间中添加一个换行符,然后将下一行输入附加到模式中空间。”这使得模式空间“这一行和下一行”。

    P

    打印到第一个换行符之前的模式空间部分。

    所以,/^\(.*\)\n\1$/!P; 意味着将第一行从开头复制到换行符并将其用作 \1 反向引用作为模式的一部分,这意味着匹配 2 个相同的连续行。当那些不匹配时(/!),打印第一行。如果它们 匹配 匹配,则不会打印。

    D

    如果模式空间不包含换行符,则开始一个正常的新循环,就像发出了 d 命令一样。否则,删除模式空间中的文本直到第一个换行符,然后使用生成的模式空间重新开始循环,而不读取新的输入行。

    模式末尾的 D 正在从第一行移出,但将第二行留在模式空间中。

    从字面上看,它是遍历文件,比较连续的行,并且仅在它们不相同时才打印它们。

    稍微更简单的版本(例如,可能更容易阅读,尽管它仍在做几乎同样多的工作)

     sed '$!N; /^!\n!$/!P; D'
    

    这对球棒模式进行了硬编码,并且至少不存储和使用反向引用,尽管它仍然在所有行中移动。当我们看到一个单独的球棒时,我们可以通过仅堆叠线条来减少这项工作:

    sed '/^!$/N; /^!\n!$/!P; D'
    

    P 和 D 仍在做他们的事。 :)

    所以我们回到了我在顶部发布的模式。 希望对您有所帮助。

    【讨论】:

      【解决方案5】:

      sed 用于做 s/old/new,仅此而已

      $ awk '$0!=p || $0!="!"; {p=$0}' file
      !interface vlan200
      !description
      !
      interface vlan201
      

      以上内容适用于任何 UNIX 机器上任何 shell 中的任何 awk。它只是说如果当前行与最后一行不同,或者当前行不是!,则打印它。然后它保存当前行以针对下一行进行测试。

      【讨论】:

        【解决方案6】:

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

        sed 'N;/^!\n!$/!P;D' file
        

        打开一个两行的窗口,如果第一行和第二行以! 开头和结尾,则删除(不打印)第一行。

        【讨论】:

          猜你喜欢
          • 2011-05-02
          • 1970-01-01
          • 2010-10-16
          • 1970-01-01
          • 2012-04-23
          • 1970-01-01
          • 1970-01-01
          • 2013-06-08
          • 1970-01-01
          相关资源
          最近更新 更多