我有多个文件没有标题,前四列相同,第五列不同。我必须使用awk将所有第五列的所有第五列以及如下所示的相应标题附加到单个最终制表符分隔的文本文件中。
File_1.txt
chr1 101845021 101845132 A 0
chr2 128205033 128205154 B 0
chr3 128205112 128205223 C 0
chr4 36259133 36259244 D 0
chr5 36259333 36259444 E 0
chr6 25497759 25497870 F 1
chr7 25497819 25497930 G 1
chr8 25497869 25497980 H 1
File_2.txt
chr1 101845021 101845132 A 6
chr2 128205033 128205154 B 7
chr3 128205112 128205223 C 7
chr4 36259133 36259244 D 7
chr5 36259333 36259444 E 10
chr6 25497759 25497870 F 11
chr7 25497819 25497930 G 11
chr8 25497869 25497980 H 12
File_3.txt
chr1 101845021 101845132 A 41
chr2 128205033 128205154 B 41
chr3 128205112 128205223 C 42
chr4 36259133 36259244 D 43
chr5 36259333 36259444 E 47
chr6 25497759 25497870 F 48
chr7 25497819 25497930 G 48
chr8 25497869 25497980 H 49
预期输出文件Final.txt
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
答案 0 :(得分:2)
如果可以安全地假设每个文件中的行的顺序相同,那么您可以非常简洁地完成这项工作:
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
即可。但脚本很脆弱。他们在所示的生态系统中工作,但不是很普遍。必要的修复并不难以置信;但是,对于必须编码来说,它们是令人讨厌的。
扩展前一个脚本以处理任意数量的文件,并输出制表符分隔数据而不是格式化(可读)数据并不是很困难。
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
答案 1 :(得分:1)
假设两个文件都已排序,您可以使用join
命令:
join -o "1.1,1.2,1.3,1.4,2.5,2.6,1.5" file3 <(join -o "1.1,1.2,1.3,1.4,1.5,2.5" file1 file2)
-o
选项允许通过从两个文件中选择某些字段来格式化输出结果。 1.x
和2.x
指的是给定的文件。例如,1.1
指的是第一个文件的第一个字段。
由于join
只接受2个文件,因此bash运算符<(...)
用于创建临时文件。
使用paste
和awk
的另一种解决方案(仍假设文件已排序):
paste file* | awk '{print $1,$2,$3,$4,$5,$10,$15}'