【问题标题】:sed editing multiple linessed 编辑多行
【发布时间】:2015-05-21 01:56:44
【问题描述】:

当涉及到多行编辑时,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 是强制性的。

【问题讨论】:

  • 我应该提到,结果应该看起来和显示的完全一样。不是全部在一条线上。另一点是,将模式空间写入文件,并将其作为一个衬里(连续字符“\”和(可能)删除空格。原因:在随后的 sed-run 中,所有 #-signs 应该被删除再次启用线路。
  • 我应该省略第二个任务,提取一个衬里,因为这会误导我想在我的编辑结果中包含一个衬里。结果应该和原来的一样,尽管每一行前面都有一个#。
  • 所以...您希望一个脚本注释掉 RECORD 块,而另一个脚本将它们放在一行中,我理解正确吗?
  • 可以这么说,是的。如果不能一次性完成,例如在文件编辑中进行并将一个衬里写入标准输出。其他解决方案是先运行 grep - 在进行编辑之前 - 并收集所有记录。
  • 它可以一次性完成(我添加了一些关于此的内容),但在这一点上,事情变得有点难看。这是挑选毒药的老游戏。在第二遍编辑文件之前,我可能会使用两次并提取一次记录。

标签: sed pattern-matching sh multiline


【解决方案1】:
 sed '/^RECORD.*\\$/,/[^\\]$/ s/^/#/
      s/^RECORD.*/#&/' YourFile

在@Wintermute 的几次评论和来自 OP 的更多信息之后

假设:

  • 以RECORD 开头的行是修改下一行的触发器
  • 结构相同(没有\ 的行直接跟在RECORD 行之后或空行)

解释:

  • 获取以RECORD 开头并以\ 结尾的行块
    • 在每行前面添加#
  • 取行(所以在对早期块进行最终修改后,只留下没有\的记录行或没有记录的行),如果以RECORD开头,则在开头添加#

【讨论】:

  • 如果块后有空行,我认为这不会起作用,因为[^\\]$ 不会匹配它。也许是\(^\|[^\\]\)$?
  • 在我的 AIX 上进行了测试,它可以工作(主要是因为我认为它不能有一条带有错误结尾行的行)。如果发生这种情况,空行将被 注释,这不是最糟糕的情况,也不是与要求相反,但在这种情况下您的解决方案会更好
  • 是的,再想想,这有点极端。不过,里面的/^RECORD/ 支票让我有点吃惊;该行也应该被注释掉,不是吗?我在想/^RECORD.*\\$/,/\(^\|[^\\]\)$/ s/^/#/,如果我们去掉“把东西放在一条线上”这一点。
  • 加上额外的解释,这段代码不再适合,我调整了一下以包含任何 RECORD 行 block
  • 模式范围本来是一个很好的技巧,除了把东西放在一条线上。不过,对于将来遇到类似问题的人来说,它会很有用,所以增加了。
【解决方案2】:

不清楚您如何识别要注释掉的块,因此我将使用以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 +xing 并调用./foo.sed filename。

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

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 命令的目标。

【讨论】:

  • 你得到它的速度比我的快,而且有完整的解释。可以使用` check at end of line with RECORD` 来增强第一个模式
  • RECORD 是识别模式开始的关键字。除最后一行外,每一行的末尾都有“\”。
  • 那么这应该完全按照要求工作。 @NeronLeVelu 我不完全确定“在行尾检查记录”是什么意思。
  • htlm 吃掉我的反斜杠 :-( 我的意思是 ^RECORD.*\\$ 作为第一个模式
  • 感谢有关写入标准输出的扩展。我也会实现的。一切都解决了。太好了!
猜你喜欢
  • 2015-09-20
  • 1970-01-01
  • 1970-01-01
  • 2018-12-17
  • 2016-07-19
  • 2016-02-03
  • 2022-07-12
  • 2020-09-21
  • 2012-09-23
相关资源
最近更新 更多