如何反转文本文件中重复块内的行(使用 sed/awk?)

如何反转文本文件中重复块内的行(使用 sed/awk?)

我有一个这样的文件:

x = {
   y = {
       z = {
           block = {
              line1
              line2
              line3
           }
        }
    }
}
x2 = {
     y2 = {
         block = {
              line4
              line5
         }
     }
}
xyz
block = {
      line6
}

等等。我需要反转块内的行,但其他所有内容都应保持顺序,如下所示:

x = {
   y = {
       z = {
           block = {
              line3
              line2
              line1
           }
        }
    }
}
x2 = {
     y2 = {
         block = {
              line5
              line4
              
         }
     }
}
xyz
block = {
      line6
}

如何使用 sed 或 awk 做到这一点?我设法将这些块颠倒过来,但无法将它们放回原位:

sed -n '/block = {.$/,/}$/p' inputfile | tac

答案1

如果你对 Perl 没问题:

perl -ne 'if($f && /}/){$f=0; print @blk};
          $f ? unshift(@blk, $_) : print;
          if(/block = {/){$f=1; @blk=()}' ip.txt
  • if(/block = {/){$f=1; @blk=()}如果输入行包含,则初始化@blk数组并设置标志$fblock = {
  • $f ? unshift(@blk, $_) : print如果该标志处于活动状态,则在数组的前面插入输入行@blk,否则打印输入行
  • if($f && /}/){$f=0; print @blk}如果该标志处于活动状态并且输入行包含,则取消设置该标志并打印数组}的内容@blk

awk

awk 'f && /}/{f=0; for(i=c; i>=1; i--) print blk[i]}
     {if(f) blk[++c] = $0; else print}
     /block = {/{f=1; c=0}' ip.txt

答案2

另一种perl方法,依靠缩进来找到}相应的结束block = {

perl -C -0777 -pe '
  s{^(\h*)block = \{\n\K.*?\n(?=\1\}$)}{
    join "", reverse $& =~ m{.*\n}g
  }gems' < your-file

答案3

证明sed代码可以很容易编写,但很难阅读:

sed '/block = {/,/}/{/[{}]/!{G;h;d;};x;s/.$//p;g;s/.*//;x;}' file

这个概念是通过附加当前的保留空间来收集保留空间中要反转的行(没有大括号的行)(因此顺序会自动更改)。该代码是可移植的,可与任何sed实现一起使用。详细地:

  • /block = {/,/}/block = {选择从到下一个 的每个范围},就像您自己所做的那样。所有其他类似的代码xyz均不加更改地打印
  • /[{}]/!{G;h;d;}处理不带大括号的行(块行):G附加当前保留空间,h将结果移动到保留空间并d停止处理带有输出的该行
  • 其余部分仅针对支撑线执行。这就是需要通过x更改缓冲区来打印保留空间中的行的地方,然后s/.$//p删除通过附加到空保留空间而引入的保留空间中的尾随换行符。 ubstitude 命令p的选项仅s在成功替换时打印,以避免打印空的保留空间。最后用于g从保留空间恢复当前行
  • 最后我们需要清空保持空间:s/.*//;x:将当前行保存到h旧空间,清空模式空间和交换缓冲区,因此保持空间将是空的,而模式空间再次包含当前行以进行输出。

答案4

使用(以前称为 Perl_6)

~$ raku -ne 'BEGIN my @blk; if (/block/ && .put) { for lines() {
             if /\}/  { put join "\n", (@blk.pop xx @blk.elems, $_); last};  
             @blk.push: $_ }
             } else { .put };'   file

#OR (more simply)

~$ raku -ne 'BEGIN my @blk; if (/block/ & .put) { for lines() { 
             if /\}/ { .put for (@blk.pop xx @blk.elems, $_).flat; last};  
             @blk.push: $_ }
             };'  file

上面的代码是用 Raku(一种 Perl 家族语言)编写的。使用-ne非自动打印标志逐行读取输入。在BEGIN块内,@blk声明一个数组,if遇到“块”字符串(关键字),lines并读取到最后的}右花括号。当它们被读取时,linespush编辑到@blk数组中,并在找到结束 Curliepop时完全关闭(即以相反的顺序)。}last

输入示例:

x = {
   y = {
       z = {
           block = {
              line1
              line2
              line3
           }
        }
    }
}
x2 = {
     y2 = {
         block = {
              line4
              line5
         }
     }
}
xyz
block = {
      line6
}

示例输出:

x = {
   y = {
       z = {
           block = {
              line3
              line2
              line1
           }
        }
    }
}
x2 = {
     y2 = {
         block = {
              line5
              line4
         }
     }
}
xyz
block = {
      line6
}

附录:对于任何寻求 @Sundeep 优秀 Perl5 解决方案的 Raku 翻译的人来说,这里是:

~$ raku -ne 'BEGIN my @blk; state $f; 
             if ($f && /\}/) {$f=0; .put for @blk}; 
             $f ?? unshift(@blk, $_) !! .put; 
             if (/block/) {$f=1; @blk=()};'  file

https://raku.org

相关内容