sed,awk或perl:模式范围匹配,打印45行然后添加记录分隔符

时间:2012-03-23 17:24:06

标签: perl sed awk

我有一个包含由模式/ #matchee /分隔的记录的文件。这些记录的长度各不相同......比如说45到75行。他们需要 ALL 为45行并仍然保持记录分隔符。记录可以来自不同的部门,部门名称在第2行的空行后面。所以记录分隔符可以简单地被认为是/ ^ #matchee /或/ ^ matchee /后跟\ n。此问题的 Deluxe 版本和 Walmart 版本......

DELUXE EDITION

按模式范围拉每条记录,这样我就可以按部门对记录进行排序。例如,用sed

sed -n '/^DEPARTMENT NAME/,/^#matchee/{p;}' mess-o-records.txt

然后,打印仅文件中每条记录的前45行以符合     45线约束。

最后,确保结果仍然在第45行有记录分隔符。

WALMART EDITION

与上面相同,但不使用范围,只需使用记录分隔符。

状态

我的尝试可能会澄清我想要做的事情。

sed -n -e '/^DEPARTMENT-A/,/^#matchee/{p;}' -e '45q' -e '$s/.*/#matchee/' mess-o-records.txt

当然,这不起作用,因为sed在每个命令的整个文件上运行。 我需要它来操作每个范围匹配而不是整个文件

SAMPLE INPUT - 80行(截断空格)

<blank line>
DEPARTMENT-A
Office space 206
Anonymous, MI 99999

Harold O Nonymous
Buckminster Abbey
Anonymous, MI 99999

item A     Socket B     45454545
item B     Gizmo Z      76767676
<too many lines here>
<way too many lines here>  


#matchee

SAMPLE OUTPUT - 现在只有45行

<blank line>
DEPARTMENT-A
Office space 206
Anonymous, MI 99999

Harold O Nonymous
Buckminster Abbey
Anonymous, MI 99999

item A     Socket B     45454545
item B     Gizmo Z      76767676
<Record now equals exactly 45 lines>  
<yet record delimiter is maintained>

#matchee

澄清更新

如果这让事情变得更容易,我将永远不会需要超过前40行。也许这个过程会是:

  • 匹配模式
  • 先打印40行。
  • 垫到合适的长度。例如,45行。
  • 重新打开定界符。例如,#matchee

我认为这会更灵活 - 也就是说,可以处理短于45行的记录。

这是基于@ Borodin的Perl示例的重复段落:

my $count = 0;
$/ = "#matchee";    

while (<>) {
    if (/^REDUNDANCY.*DEPT/) {
        print;
        $count = 0;
    }   
    else {
        print if $count++ < 40; 
        print "\r\n" x 5; 
        print "#matchee\r\n";
    }   
}

这为每条记录添加了5个换行符+分隔模式/ #matchee /。所以这是错的 - 但它说明了我想要的东西。

根据部门 - 打印 - 分隔符打印40行。

4 个答案:

答案 0 :(得分:2)

我想我明白你想要什么。不确定按模式范围拉出每条记录#matchee后面跟着一个空白行,然后是部门行吗?所以实际上记录了2号?

这个Perl片段做了我理解你需要的东西。

如果您愿意,可以将输入文件放在命令行上并放弃open调用。然后循环必须是while (<>) { ... }

让我们知道到目前为止是否正确,以及您还需要它。

use strict;
use warnings;

open my $fh, '<', 'mess-o-records.txt' or die $!;

my $count = 0;

while (<$fh>) {
  if (/^#matchee/) {
    print;
    $count = 0;
  }
  else {
    print if $count++ < 45;
  }
}

答案 1 :(得分:1)

我知道这已经有了一个公认的答案,但我想我会为任何感兴趣的人发布一个awk例子。这不是100%,但它完成了工作。

注意这会对行进行编号,以便您可以验证脚本是否按预期工作。从i,移除print i, current[i]以删除行号。

dep.awk

BEGIN { RS = "#matchee\n\n" }

$0 ~ /[a-zA-Z0-9]+/ {
    split($0, current, "\n")
    for (i = 1; i <= 45; i++) {
        print i, current[i];
    }
    print "#matchee\n"
}

在此示例中,您可以通过将记录分隔符( RS )设置为“#matchee \ n \ n”来开始脚本。有两个换行符,因为第一行结束了#matchee出现的行,第二行是空行。

匹配验证记录包含有效的字母或数字。您还可以检查匹配是否以'DEPARTMENT-'开头,但如果存在偏离的换行符,则会失败。检查内容是最安全的途径。因为它使用块记录(即DEPARTMENT-A到#matchee),你可以通过awk或sed再次传递 $ 0 ,或者使用awk split函数并循环45行。在awk中,数组不是零索引。

print函数包含换行符,因此块仅以print "#matchee\n"结尾,而不是记录分隔符变量中的双\n

您也可以将相同的awk脚本放入bash脚本中,并更改行数和字段分隔符。当然,你应该添加验证和诸如此类的东西,但这是开始:

<强> dep.sh

#!/bin/bash
# prints the first n lines within every block of text delimited by splitter
splitter=$1
numlines=$2

awk 'BEGIN { RS="'$1'\n\n" }
$0 ~ /[a-zA-Z0-9]+/ {
    split($0, current, "\n")
    for(i=1;i<='$numlines';i++) {
        print i, current[i]
    }
    print "'$splitter'", "\n"
}' $3

使脚本可执行并运行它。

./dep.sh '#matchee' 45 input.txt > output.txt

我已将这些文件添加到要点中,因此您还可以验证output

答案 2 :(得分:0)

这可能对您有用:

D="DEPARTMENT-A" M="#matchee"
sed '/'"$D/,/$M"'/{/'"$D"'/{h;d};H;/'"$M"'/{x;:a;s/\n/&'"$M"'/45;tb;s/'"$M"'/\n&/;ta;:b;s/\('"$M"'\).*/\1/;p};d}' file

说明:

  • 专注于行的范围/DEPARTMENT/,/#matchee/
    • 在范围开始时移动模式空间(PS)以保留空间(HS)并删除PS /DEPARTMENT/{h;d}
    • 范围内的所有后续行都附加到HS并删除H....;d
    • 范围结束时:/#matchee/
      • 转换为HS x
      • 测试范围内的45行,如果成功,则在第45行追加#matchee s/\n/&#matchee/45
      • 如果先前的替换成功,则转移到标签btb
      • 如果先前的替换不成功,请在#matchee s/'"$M"'/\n&/之前插入换行符,从而将短记录延长至45行。
      • 分支到标签a并测试45行等。 ta
      • 将第一次出现的#matchee替换为该行的自身。 s/\('"$M"'\).*/\1/因此将长记录缩短为45行。
      • 打印记录范围。 p
  • 所有非范围记录均未触及。

答案 3 :(得分:0)

TXR解决方案(http://www.nongnu.org/txr

为了便于说明,使用假数据,我将需求从40行缩短为12行。我们找到以部门名称开头的记录,由#matchee分隔。我们抛弃它们,切成不超过12行,再次添加#matchee

@(collect)
@  (all)
@dept
@  (and)
@    (collect)
@line
@    (until)
#matchee
@    (end)
@  (end)
@(end)
@(output)
@  (repeat)
@{line[0..12] "\n"}
#matchee
@  (end)
@(end)

这里,dept变量应该来自-D命令行选项,但当然可以更改代码以接受它作为参数并在缺少时使用它

运行样本数据:

$ txr -Ddept=DEPARTMENT-A trim-extract.txr mess-o-records.txt 
DEPARTMENT-A
Office space 206
Anonymous, MI 99999

Harold O Nonymous
Buckminster Abbey
Anonymous, MI 99999

item A     Socket B     45454545
item B     Gizmo Z      76767676

<too many lines here>
#matchee

DEPARTMENT-A之前的空行消失,正好有12行,恰好包含<too many ...>垃圾的一行。

请注意@(until)的语义是这样的,#matchee从收集的材料中排除。因此,无条件地将其添加到@(output)子句中是正确的。即使记录恰好在找到#matchee之前的12行,该程序也能正常工作。

如果找不到#matchee,它将与记录不匹配。