我有一个包含由模式/ #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行。也许这个过程会是:
我认为这会更灵活 - 也就是说,可以处理短于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行。
答案 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/
/DEPARTMENT/{h;d}
H....;d
/#matchee/
x
#matchee
s/\n/&#matchee/45
b
。 tb
#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
,它将与记录不匹配。