拆分具有多个标题行的文件

  • 本文关键字:文件 标题 拆分 awk
  • 更新时间 :
  • 英文 :


我正试图用以下代码拆分一个具有多个标题行的大文件。虽然正在工作;但是在分割文件中的头之后引入空行。如何去掉空白行?

/^@/ {
hdr = hdr $0 ORS
next
}
split($NF, a, ":")
!seen[a[3]]++{
out = a[3] ".txt"
print hdr > out
}
{out = a[3] ".txt"
print >> out 
close(f)}

输入文件

@Loc: Cali
@Yr: 2017
@ST: blood
@header information
S1  yes yes no  yes 3y  Mother Z:AgeGroup:A1
S17 yes no  no  yes 27y Mother Z:AgeGroup:A2
B13 no  no  no  yes 1y  Mother Z:AgeGroup:A3
B5  yes yes yes yes 76y Mother Z:AgeGroup:A1
D1  yes yes no  no  18y Mother Z:AgeGroup:A1

预期输出

A1.txt

@Loc: Cali
@Yr: 2017
@ST: blood
@header information
S1  yes yes no  yes 3y  Mother Z:AgeGroup:A1
B5  yes yes yes yes 76y Mother Z:AgeGroup:A1
D1  yes yes no  no  18y Mother Z:AgeGroup:A1

A2.txt

@Loc: Cali
@Yr: 2017
@ST: blood
@header information
S17 yes no  no  yes 27y Mother Z:AgeGroup:A2

A3.txt

@Loc: Cali
@Yr: 2017
@ST: blood
@header information
B13 no  no  no  yes 1y  Mother Z:AgeGroup:A3

但我的脚本得到的是

A1.txt

@Loc: Cali
@Yr: 2017
@ST: blood
@header information
S1  yes yes no  yes 3y  Mother Z:AgeGroup:A1
B5  yes yes yes yes 76y Mother Z:AgeGroup:A1
D1  yes yes no  no  18y Mother Z:AgeGroup:A1

A2.txt

@Loc: Cali
@Yr: 2017
@ST: blood
@header information
S17 yes no  no  yes 27y Mother Z:AgeGroup:A2

A3.txt

@Loc: Cali
@Yr: 2017
@ST: blood
@header information
B13 no  no  no  yes 1y  Mother Z:AgeGroup:A3

使用您显示的示例,请尝试以下awk代码。使用您显示的示例编写和测试,应该可以在任何awk版本中工作(尽管我在GNUawk中进行了测试(。

awk '
BEGIN  { FS=OFS=":" }
FNR==NR{
header=(header?header ORS:"") $0
next
}
prev!=$NF{
close(outputFile)
outputFile=$3".txt"
print header ORS > (outputFile)
}
{
print > (outputFile)
prev=$NF
}
' <(awk '/^@/{print;next} {exit}' Input_file) <(grep -v '^@' Input_file | sort -t: -k1.1)

代码说明:在此处添加上述代码的详细说明。

  • 首先,我正在运行代码awk '/^@/{print;next} {exit}' Input_file(将最后一行的第一个参数检入主awk(。在我只打印从@开始并尽快退出的行的情况下,任何非@行都会按照所示示例出现。该输出作为第一组输入发送到主awk
  • 然后我运行代码grep -v '^@' Input_file | sort -t: -k1.1,这样我就可以简单地获得不是从@开始的每一行,作为主awk的第二个输入
  • 现在在主awk代码中:我正在使用条件FNR==NR,当读取第一个输入(上面在我的第一点中提到(时,它将为TRUE。在这段代码中,我创建了一个名为headerawk变量,它的所有行都用新行分隔,以便以后使用
  • 这里next非常重要,因为从这里开始,它将跳过所有进一步的语句,直到FNR==NR为TRUE
  • 一旦第一个输入完成读取,第二个输入正在读取,那么简单地检查prev是否不等于当前行的第三个字段,然后简单地在后端closing输出文件(我们需要获得输出的地方(,基本上可以避免too many files opened错误。将当前第三个字段的值设置为输出文件名
  • 在其他情况下,如果第三个字段/列的值相同,则只需将当前行打印到outputFile变量(其中包含输出文件名(中,并将当前$3的值分配给下一行要检查的outputFile
$ cat tst.awk
BEGIN { FS=":" }
/^@/ {
hdr = hdr $0 ORS
next
}
!($NF in out) {
out[$NF] = $NF ".txt"
printf "%s", hdr > out[$NF]
}
{
print >> out[$NF]
close(out[$NF])
}

$ awk -f tst.awk file

$ head *.txt
==> A1.txt <==
@Loc: Cali
@Yr: 2017
@ST: blood
@header information
S1  yes yes no  yes 3y  Mother Z:AgeGroup:A1
B5  yes yes yes yes 76y Mother Z:AgeGroup:A1
D1  yes yes no  no  18y Mother Z:AgeGroup:A1
==> A2.txt <==
@Loc: Cali
@Yr: 2017
@ST: blood
@header information
S17 yes no  no  yes 27y Mother Z:AgeGroup:A2
==> A3.txt <==
@Loc: Cali
@Yr: 2017
@ST: blood
@header information
B13 no  no  no  yes 1y  Mother Z:AgeGroup:A3

你的代码中最大的问题是split($NF, a, ":")行,它会触发awk打印当前行,所以当你运行它时,你一定看到了所有输入都在屏幕上回响。这一点在你的问题中值得一提。如果你真的想以这种方式使用split(),那么你应该把它放在一个动作块中,而不是作为一个条件,即{ split($NF, a, ":") }

除此之外:

  1. 您的代码中有close(f),但没有名为f的变量,所以这什么都没做,让您面临";打开的文件太多";错误或执行速度降低,以及
  2. 当您将ORS附加到每个标题行的末尾时填充hdr,然后使用print再次添加而不是printf来输出hdr,从而获得额外的打印每个hdr块后的空白行

由于每次在hdr字段中都会附加ORS,然后在使用print hdr > out时,它会在最后附加另一个换行符。

你应该像这样使用printf

printf "%s", hdr > out

最新更新