sed编辑多行

时间:2015-03-18 10:21:06

标签: sed pattern-matching sh multiline

对于多行编辑,sed编辑对我来说总是一个新的挑战。在这种情况下,我有以下模式:

RECORD 4,4 ,5,48 ,7,310 ,10,214608 ,12,199.2 ,13,-19.2 ,15,-83 ,17,35 \
     ,18,0.8 ,21,35 ,22,31.7 ,23,150 ,24,0.8 ,25,150 ,26,0.8 ,28,25 ,29,6 \
     ,30,1200 ,31,1 ,32,0.2 ,33,15 ,36,0.4 ,37,1 ,39,1.1 ,41,4 ,80,2 \
     ,82,1000 ,84,1 ,85,1

我想转换成:

#RECORD 4,4 ,5,48 ,7,310 ,10,214608 ,12,199.2 ,13,-19.2 ,15,-83 ,17,35 \
#   ,18,0.8 ,21,35 ,22,31.7 ,23,150 ,24,0.8 ,25,150 ,26,0.8 ,28,25 ,29,6\
#   ,30,1200 ,31,1 ,32,0.2 ,33,15 ,36,0.4 ,37,1 ,39,1.1 ,41,4 ,80,2 \
#   ,82,1000 ,84,1 ,85,1

除此之外,我想保留这4行的全部(可能多于或少于4行(在输入中出现不可预测)到一条(长)行而没有反斜杠或换行。

一个人可以说两个任务。

sed是强制性的。

2 个答案:

答案 0 :(得分:3)

你不清楚如何识别要注释掉的块,所以我会使用以RECORD开头的行中的块并处理只要有反斜杠的块结束(如果您的要求不同,使用的模式将需要相应修改)。

为此,您可以使用

sed '/^RECORD/ { :a /\\$/ { N; ba }; s/[[:space:]]*\\\n[[:space:]]*/ /g; s/^/#/ }' filename

其工作原理如下:

/^RECORD/ {                            # if you find a line that starts with
                                       # RECORD:
  :a                                   # jump label for looping
  /\\$/ {                              # while there's a backslash at the end
                                       # of the pattern space
    N                                  # fetch the next line
    ba                                 # loop.
  }
                                       # After you got the whole block:
  s/[[:space:]]*\\\n[[:space:]]*/ /g   # remove backslashes, newlines, spaces
                                       # at the end, beginning of lines
  s/^/#/                               # and put a comment sign at the
                                       # beginning.
}

附录:为了保持线条结构的完整性,请使用

sed '/^RECORD/ { :a /\\$/ { N; ba }; s/\(^\|\n\)/&#/g }' filename

这种方法的工作原理大致相同,只是删除了换行符,并在每个换行符后插入注释符号(并在开头一次)。

附录2:只需将RECORD块放在一行:

sed '/^RECORD/ { :a /\\$/ { N; ba }; s/[[:space:]]*\\\n[[:space:]]*/ /g }' filename

这只是删除s/^/#/位的第一个脚本。

附录3:要在将RECORD块同时放入一行时隔离它们,

sed -n '/^RECORD/ { :a /\\$/ { N; ba }; s/[[:space:]]*\\\n[[:space:]]*/ /g; p }' filename

-n标志禁止正常的默认打印操作,p命令替换我们要打印的那些行。

将这些记录写入文件,同时在正常输出中将它们注释掉,

sed -e '/^RECORD/ { :a /\\$/ { N; ba }; h; s/[[:space:]]*\\\n[[:space:]]*/ /g; w saved_records.txt' -e 'x; s/\(^\|\n\)/&#/g }' foo.txt

这里真的有新东西。简短注释:

#!/bin/sed -f

/^RECORD/ {
  :a
  /\\$/ {
    N
    ba
  }
                                      # after assembling the lines
  h                                   # copy them to the hold buffer
  s/[[:space:]]*\\\n[[:space:]]*/ /g  # put everything on a line
  w saved_records.txt                 # write that to saved_records.txt
  x                                   # swap the original lines back
  s/\(^\|\n\)/&#/g                    # and insert comment signs
}

直接在命令行中指定此代码时,必须将其拆分为多个-e选项,因为w命令未被;终止。

将代码放入自己的文件(例如foo.sed)并运行sed -f foo.sed filename时,不会出现此问题。或者,对于高级版,将#!/bin/sed -f shebang放在文件的顶部,chmod +x然后调用./foo.sed filename

最后,要就地编辑输入文件并将记录打印到stdout,可以修改如下:

sed -i -e '/^RECORD/ { :a /\\$/ { N; ba }; h; s/[[:space:]]*\\\n[[:space:]]*/ /g; w /dev/stdout' -e 'x; s/\(^\|\n\)/&#/g }' filename

这里的新内容是-i标志,用于现场编辑文件,并/dev/stdout作为w命令的目标。

答案 1 :(得分:2)

 sed '/^RECORD.*\\$/,/[^\\]$/ s/^/#/
      s/^RECORD.*/#&/' YourFile

经过几次关于@Wintermute的评论以及来自OP的更多信息

假设:

  • 在开始时与RECORD对齐的行是修改下一行的触发器
  • 结构相同(没有\行,RECORD行直接跟随或空行

说明:

  • RECORD开头并以\结尾的行块
    • 在每行前面添加#
  • 取行(所以在最后一个块的最终修改之后,只留下没有\的RECORD行而没有记录)并且如果以{{1开头,则在开头添加# }}