文件顺序相同
如果可以安全地假设每个文件中的行顺序相同,那么您可以相当简洁地完成这项工作:
awk '
FILENAME != oname { FN++; oname = FILENAME }
{ p[FNR] = $1; s[FNR] = $2; e[FNR] = $3; n[FNR] = $4; f[FN,FNR] = $5; N = FNR }
END {
printf("%-8s %-12s %-12s %-4s %-5s %-5s %-5s\n",
"Part", "Start", "End", "Name", "File1", "File2", "File3");
for (i = 1; i <= N; i++)
{
printf("%-8s %-12d %-12d %-4s %-5d %-5d %-5d\n",
p[i], s[i], e[i], n[i], f[1,i], f[2,i], f[3,i]);
}
}' file_1.txt file_2.txt file_3.txt
当你开始一个新文件时,第一行出现,并增加 FN 变量(因此文件 1 中的行可以用 FN == 1 等标记)。它将文件名记录在oname 中,以便发现更改。
第二行对每个数据行进行操作,将前四个字段存储在数组p、s、e、n(按当前文件内的记录号索引),并记录第五f 中的列(由 FN 和记录号索引)。它记录了N中当前文件中的当前记录号。
END 块打印出标题,然后为数组中的每一行(索引从 1 到 N)打印出各个字段。
输出是(不出所料):
Part Start End Name File1 File2 File3
chr1 101845021 101845132 A 0 6 41
chr2 128205033 128205154 B 0 7 41
chr3 128205112 128205223 C 0 7 42
chr4 36259133 36259244 D 0 7 43
chr5 36259333 36259444 E 0 10 47
chr6 25497759 25497870 F 1 11 48
chr7 25497819 25497930 G 1 11 48
chr8 25497869 25497980 H 1 12 49
不同顺序的文件
如果您不能依赖每个文件中的记录顺序相同,那么您必须更加努力。假设第一个文件中的记录按要求的顺序排列,以下脚本安排按顺序打印记录:
awk '
FILENAME != oname { FN++; oname = FILENAME }
{ key = $1 SUBSEP $2 SUBSEP $3 SUBSEP $4
if (FN == 1)
{ p[key] = $1; s[key] = $2; e[key] = $3; n[key] = $4; f[FN,key] = $5; k[FNR] = key; N = FNR }
else
{ if (key in p)
f[FN,key] = $5
else
printf "Unmatched key (%s) in %s\n", key, FILENAME
}
}
END {
printf("%-8s %-12s %-12s %-4s %-5s %-5s %-5s\n",
"Part", "Start", "End", "Name", "File1", "File2", "File3")
for (i = 1; i <= N; i++)
{
key = k[i]
printf("%-8s %-12d %-12d %-4s %-5d %-5d %-5d\n",
p[key], s[key], e[key], n[key], f[1,key], f[2,key], f[3,key])
}
}' "$@"
这与之前的脚本密切相关; FN 处理是相同的。 SUBSEP 变量用于分隔多索引数组中的下标。变量key 包含相同的值
通过索引数组z[$1,$2,$3,$4] 生成。
如果处理第一个文件 (FN == 1),则会创建数组 p、s、e、n 中的值,并由 key 索引。第五列同样记录在f。文件中键出现的顺序记录在数组k中,以(文件)记录号为索引。
如果正在处理第二个或第三个文件,请检查密钥是否已知,如果不知道则报告。假设已知,再次在f 中添加第五列。
打印类似,只是从k依次收集键,然后打印相关值。
鉴于这些文件:
-
file_4.txt
chr8 25497869 25497980 H 1
chr7 25497819 25497930 G 1
chr6 25497759 25497870 F 1
chr5 36259333 36259444 E 0
chr4 36259133 36259244 D 0
chr3 128205112 128205223 C 0
chr2 128205033 128205154 B 0
chr1 101845021 101845132 A 0
-
file_5.txt
chr2 128205033 128205154 B 7
chr8 25497869 25497980 H 12
chr3 128205112 128205223 C 7
chr1 101845021 101845132 A 6
chr6 25497759 25497870 F 11
chr4 36259133 36259244 D 7
chr7 25497819 25497930 G 11
chr5 36259333 36259444 E 10
-
file_6.txt
chr5 36259333 36259444 E 47
chr4 36259133 36259244 D 43
chr6 25497759 25497870 F 48
chr8 25497869 25497980 H 49
chr2 128205033 128205154 B 41
chr3 128205112 128205223 C 42
chr7 25497819 25497930 G 48
chr1 101845021 101845132 A 41
脚本产生输出:
Part Start End Name File1 File2 File3
chr8 25497869 25497980 H 1 12 49
chr7 25497819 25497930 G 1 11 48
chr6 25497759 25497870 F 1 11 48
chr5 36259333 36259444 E 0 10 47
chr4 36259133 36259244 D 0 7 43
chr3 128205112 128205223 C 0 7 42
chr2 128205033 128205154 B 0 7 41
chr1 101845021 101845132 A 0 6 41
这些脚本不能完全适应许多情况。例如,如果文件的长度不同;如果有重复的键;如果在其他文件中找不到的一个或两个文件中找到密钥;如果第五列数据不是数字;如果第二列和第三列不是数字;如果只有两个文件,或者列出的文件超过三个。 “非数字”问题实际上很容易解决;只需使用%s 而不是%d。但是脚本很脆弱。它们在所示的生态系统中起作用,但不是很普遍。必要的修复并不难。不过,编写代码很麻烦。
文件可能多于或少于 3 个
扩展之前的脚本来处理任意数量的文件,并输出制表符分隔的数据而不是格式化(可读)的数据并不是很困难。
awk '
FILENAME != oname { FN++; file[FN] = oname = FILENAME }
{ key = $1 SUBSEP $2 SUBSEP $3 SUBSEP $4
if (FN == 1)
{ p[key] = $1; s[key] = $2; e[key] = $3; n[key] = $4; f[FN,key] = $5; k[FNR] = key; N = FNR }
else
{ if (key in p)
f[FN,key] = $5
else
{
printf "Unmatched key (%s) in %s\n", key, FILENAME
exit 1
}
}
}
END {
printf("%s\t%s\t%s\t%s", "Part", "Start", "End", "Name")
for (i = 1; i <= FN; i++) printf("\t%s", file[i]);
print ""
for (i = 1; i <= N; i++)
{
key = k[i]
printf("%s\t%s\t%s\t%s", p[key], s[key], e[key], n[key])
for (j = 1; j <= FN; j++)
printf("\t%s", f[j,key])
print ""
}
}' "$@"
关键是printf 不会输出换行符,除非你告诉它这样做,但print 会输出换行符。该代码记录了实际文件名以用于打印列。它循环遍历文件数据数组,假设每个文件中的行数相同。
给定 6 个文件作为输入 - 三个原始文件、第一个文件的倒序副本以及第二个和第三个文件的置换副本,输出有 6 列额外数据,列标识:
Part Start End Name file_1.txt file_2.txt file_3.txt file_4.txt file_5.txt file_6.txt
chr1 101845021 101845132 A 0 6 41 0 6 41
chr2 128205033 128205154 B 0 7 41 0 7 41
chr3 128205112 128205223 C 0 7 42 0 7 42
chr4 36259133 36259244 D 0 7 43 0 7 43
chr5 36259333 36259444 E 0 10 47 0 10 47
chr6 25497759 25497870 F 1 11 48 1 11 48
chr7 25497819 25497930 G 1 11 48 1 11 48
chr8 25497869 25497980 H 1 12 49 1 12 49