我正试图用以下代码拆分一个具有多个标题行的大文件。虽然正在工作;但是在分割文件中的头之后引入空行。如何去掉空白行?
/^@/ {
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。在这段代码中,我创建了一个名为header
的awk
变量,它的所有行都用新行分隔,以便以后使用 - 这里
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, ":") }
。
除此之外:
- 您的代码中有
close(f)
,但没有名为f
的变量,所以这什么都没做,让您面临";打开的文件太多";错误或执行速度降低,以及 - 当您将ORS附加到每个标题行的末尾时填充hdr,然后使用
print
再次添加而不是printf
来输出hdr,从而获得额外的打印每个hdr块后的空白行
由于每次在hdr
字段中都会附加ORS
,然后在使用print hdr > out
时,它会在最后附加另一个换行符。
你应该像这样使用printf
:
printf "%s", hdr > out