我有一个制表符分隔的文本文件,其中包含Column1...Columnn
列和R1
行Rn
。在某些列中,有多个嵌套字段,它们带有标识符,然后用分号分隔(在附加的示例图像文件中以红色显示)。确切地说,我附上了该文件的示例快照。
这里Column6中的数据INFO
有多个嵌套字段如DP; RPB; AF1; AC1; DP4;...
等。
任务:从inputfile.txt
Column6 中INFO
选择字段DP4=a,b,c,d
并需要对DP4=a,b,c,d
值执行简单的算术运算(在图像中标记为红色),例如(c+d)/(a+b+c+d)
-> 并将每行的结果粘贴为INFOextra
同一文件中的新列。
如何在 e unix shell 脚本中完成此操作?
答案1
awk '$1=="ID" {print $0 "\tINFOextra"; next}; NF { info=$6; gsub(/.*;DP4=|;MQ=.*/, "", info); split(info, a, /,/); print $0 "\t" (a[3]+a[4])/(a[1]+a[2]+a[3]+a[4])}' inputfile.txt > outputfile.txt
# then check the content outputfile.txt and rename it if important
如您所见,解决方案与您上一个问题的答案非常相似,而且一点也不长。这是因为 awk 经过了极其精细的调整,可以解决像您这样的问题。我建议您查看其手册页(http://linux.die.net/man/1/awk) 来了解它与其他(更通用的)语言相比有多么简单。
如果要将多个输入文件处理为具有适当名称的多个输出文件,则选项如下:
- 在 shell 中创建一个循环并逐个启动每个文件的一个 awk 进程
让 awk 将输出写入文件,这些文件的名称取决于当前输入文件的名称,这些信息存储在 awk 的 FILENAME 变量中,该变量在处理过程中自动设置。在 awk 代码中,可以使用与 shell 相同的语法和类似的结果来重定向:
awk '$1=="ID" {print $0 "\tINFOextra" > FILENAME ".out"; next}; NF { info=$6; gsub(/.*;DP4=|;MQ=.*/, "", info); split(info, a, /,/); 打印 $0 "\t" (a[3]+a[4])/(a[1]+a[2]+a[3]+a[4]) > FILENAME ".out"}' 输入文件 1.txt 输入文件 2.txt
这里,每个 inputfileN.txt 实例都会有一个对应的 inputfileN.txt.out 文件。FILENAME 是一个简单的字符串,因此对输出文件的任何操作都是有效的。
当规范变得复杂,以至于附加字段必须出现在内部位置(而不是开头或结尾)时,应考虑创建一个子例程(在 awk 中称为函数)来创建输出行。此处的函数遍历所有字段,并照常打印它们,但在附加字段应该出现的位置,它会将其写入第 n-1 个字段之后和第 n 个字段之前,使其成为第 n 个字段。现在,值得将 awk 代码放在其自己的文件中:
$ cat bio.awk
function myprint( str) {
for (i=1; i<=NF; ++i) {
printf "%s", $i > FILENAME ".out"
if (i==44)
printf "\t%s", str >> FILENAME ".out"
if (i!=NF)
printf "\t" >> FILENAME ".out"
}
print "" >> FILENAME ".out"
}
$1=="ID" {
myprint( "INFOextra")
next
}
NF {
info=$6
gsub(/.*;DP4=|;MQ=.*/, "", info)
split(info, a, /,/)
myprint( (a[3]+a[4])/(a[1]+a[2]+a[3]+a[4]) )
}
然后调用它会产生一个更短更干净的命令行:
awk -f bio.awk inputfile1.txt inputfile2.txt