【问题标题】:sed append after multi-line matchsed 在多行匹配后追加
【发布时间】:2012-05-06 23:28:40
【问题描述】:

我得到了一些看起来像这样的代码:

lots of code... 
start(){
  some code...
  if mountpart "$1" /mnt -w ; then
  some code...
}
lots of code...

我现在想使用sed 在if mountpart "$1" /mnt -w ; then 之后插入一些新代码。 由于if mountpart "$1" /mnt -w ; then 存在多次,我不能简单地在每次匹配后附加新代码。它应该只在start() { }内部第一次出现之后插入

为此使用sed 是个好主意吗?或者执行此类更改的适当工具是什么?

【问题讨论】:

    标签: file sed patch


    【解决方案1】:

    一旦您需要开始跟踪状态(例如,我在文件中的什么位置?到目前为止我看到了什么?),我通常会从 sed 切换到 perl。这是一个简单的单行代码来满足您的需要(将“NEW CODE”替换为您需要插入的新代码;将<filename> 替换为实际文件):

    perl -ne 'BEGIN{$state=0} print; if (/^\s*start\(\){/) {$state++;} if (/if mountpart "\$1" \/mnt -w ; then/) { if ($state==1) {print "NEW CODE\n"; $state++;} }'  <filename>
    

    简单分解一下:-n 使 perl 的工作有点像 sed;它会读取每一行并处理它,但是,除非你要求它,否则它不会打印该行;这让我们在(可能)输出新代码之前输出每一行。 -e 使 perl 从命令行读取程序。获取程序并重新格式化,并添加 cmets,我们得到:

    // -n runs the program for every line of input
    // but a BEGIN {} block only runs once, before input
    BEGIN {
      //where are we in the input?
      // 0=no start() yet
      // 1=start() was seen
      // 2=we have output the new code; never do it again
      $state=0
    } 
    
    print;  // print the line we just read in, unchanged
    
    if (/^\s*start\(\){/) {  //regex.  Did we see start(){ ?
      $state++;              //if so, change to next state
    }
    
    if (/if mountpart "\$1" \/mnt -w ; then/) {   //regex. did we see mountpart cmd?
      if ($state==1) {                            //if so, are we in the right state
        print "NEW CODE\n";                       //if so, output the new code
        $state++;                                 //...and change to next state 
                                                  //...so we don't output new code 2x
      } 
    }
    

    请注意,我们从不按名称引用该行...我们只是说“打印”来打印该行,并且正则表达式看起来像 sed 模式,我们假设我们正在匹配该行。那是因为-n 将每一行读入 perl 的 $_ 变量,这是 print 等函数的默认参数。这不是我编写大型 perl 程序的方式,但对于像这样的单行或简短的一次性修复脚本来说是合适的(并且是惯用的)。

    您也可以省略 &lt;filename&gt; 并像 sed 一样流式传输输入,例如:

    cat myfile.txt | perl -ne 'BEGIN...'
    

    【讨论】:

      【解决方案2】:

      我的 sed 脚本,patch.sed:

      # replace your "if then" block inside start() by another pattern 
      # to make it unrecognizable to sed in the next command
      /^start(){/,/^}/!{s/if mountpart "$1" \/mnt -w ; then/outside_start/g}
      
      # inside start(), append "NEW CODE" after "if then" block
      /if mountpart "$1" \/mnt -w ; then/a\
      NEW CODE
      
      # recover your "if then" block outside start()
      /^start(){/,/^}/!{s/outside_start/if mountpart "$1" \/mnt -w ; then/g}
      

      输入:

      lots of code... 
      if mountpart "$1" /mnt -w ; then
      lots of code
      
      start(){
        some code...
        if mountpart "$1" /mnt -w ; then
        some code...
      }
      lots of code...
      if mountpart "$1" /mnt -w ; then
      

      运行它:

      sed -f patch.sed input
      

      输出:

      lots of code...
      if mountpart "$1" /mnt -w ; then
      lots of code
      
      start(){
        some code...
      if mountpart "$1" /mnt -w ; then
          NEW CODE
      some code...
      }
      lots of code...
      if mountpart "$1" /mnt -w ; then
      

      您的“start(){”应该在一行的开头,“}”也应该与之匹配。这就是在你的 start() 中匹配的方式;

      没有脚本,您可以使用“-e”选项通过 sed 执行多次编辑。但要小心避开 $。

      【讨论】:

        【解决方案3】:

        这可能对你有用:

        sed '/^start()/,/^}/!b;x;/./{x;b};x;/if mountpart "$1" \/mnt -w ; then/!b;h;a\  NEWCODE' file
        

        如果有很多 NEWCODE,请将其放在一个文件中并运行它:

        sed '/^start()/,/^}/!b;x;/./{x;b};x;/if mountpart "$1" \/mnt -w ; then/!b;h;r newcode.txt' file
        

        解释:

        保持空间是备用寄存器。它可用于操作文本或(如在本例中)作为标志/开关以显示事件已经发生。

        • 如果文本不在start() 和} 之间,则退出/^start()/,/^}/!b
        • 检查保留空间,如果里面有任何东西,请退出x;/./{x;b}
        • 检查标记,如果没有退出x;/if mountpart "$1" \/mnt -w ; then/!b
        • 将标记字符串作为状态开关放在保持空间中h
        • 附加/读取NEWCODE a\ NEWCODE 或r newcode.txt

        【讨论】:

          【解决方案4】:

          TXR:

          @(collect)
          @prolog
          @(until)
          start(){
          @(end)
          @(collect)
          @inner
          @(until)
            if mountpart "$1" /mnt -w ; then
          @(end)
          @(collect)
          @epilog
          @(end)
          @(output)
          @{prolog "\n"}
          @{inner "\n"}
          @{epilog[0]}
          NEW TEXT
          @{epilog[1..t] "\n"}
          @(end)
          

          数据:

          lots of code... 
            if mountpart "$1" /mnt -w ; then
          lots of code
          
          start(){
            some code...
            if mountpart "$1" /mnt -w ; then
            some code...
          }
          lots of code...
            if mountpart "$1" /mnt -w ; then
          

          运行:

          $ txr insert.txr code.sh
          lots of code... 
            if mountpart "$1" /mnt -w ; then
          lots of code
          
          start(){
            some code...
            if mountpart "$1" /mnt -w ; then
          NEW TEXT
            some code...
          }
          lots of code...
            if mountpart "$1" /mnt -w ; then
          

          【讨论】:

          • 感谢您的有趣回答。由于 txr 在大多数系统中不可用,很遗憾它不适合我。
          • 它只被移植到 Linux、Windows(“native”和 Cygwin)、OS/X、FreeBSD 和 NetBSD。没有收到任何 Solaris 用户等的消息。
          • 抱歉,我的意思不是可用,而是已安装。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2022-01-17
          • 2022-01-13
          • 2013-09-30
          • 1970-01-01
          • 1970-01-01
          • 2017-02-13
          • 1970-01-01
          相关资源
          最近更新 更多