![使用 printf 和 AWK 正确格式化我的输出](https://linux22.com/image/14845/%E4%BD%BF%E7%94%A8%20printf%20%E5%92%8C%20A%E2%80%8B%E2%80%8BWK%20%E6%AD%A3%E7%A1%AE%E6%A0%BC%E5%BC%8F%E5%8C%96%E6%88%91%E7%9A%84%E8%BE%93%E5%87%BA.png)
我正在尝试使用 printf 和 awk 来格式化一些输出。基本上,我希望最终的格式看起来像这样(所有内容都以制表符分隔。)
chr10 100000624 100000625 10:100000625_A_G
chr10 100000644 100000645 10:100000645_A_C
chr10 100002463 100002464 10:100002464_C_T
chr10 100003241 100003242 10:100003242_G_T
chr10 100003303 100003304 10:100003304_A_G
chr10 10000337 10000338 10:10000338_C_T
chr10 100003515 100003516 10:100003516_A_G
chr10 100003784 100003785 10:100003785_C_T
chr10 100004359 100004360 10:100004360_A_G
chr10 100004440 100004441 10:100004441_C_G
...
起始文件如下所示(其他列并不重要):
10:100000625_A_G G A
10:100000645_A_C C A
10:100002464_C_T C T
10:100003242_G_T G T
10:100003304_A_G G A
10:10000338_C_T T C
10:100003516_A_G A G
10:100003785_C_T C T
10:100004360_A_G A G
10:100004441_C_G C G ...
...
第二列应该比第一列少一列。使用我的原始文件,我基本上做了:
awk -F ":" '$1=$1' OFS="\t" <(zcat <filename>) | awk -F "_" '$2=$2' OFS="\t" | awk -v OFMT="%f" 'BEGIN {OFS=FS="\t"} {print "chr"$1, $2-1, $2, $1":"$2"_"$3"_"$4}'
首先,我将 id 的开头部分分解为“:”,然后分解为“_”。我想我也可以awk -F "[:_]"...
同时使用这两个分隔符来完成并分解第一列,但我认为这最终不会产生影响。
这是有效的,除了在少数情况下,数字以科学记数法报告(数字如 12000000),这是我不想要的。 printf 应该能够让我撤消科学记数法,但我无法让它工作。
我的第一个想法是我希望第一列和第四列作为字符串,第二列和第三列作为没有小数的浮点数字。所以,我尝试了以下方法awk '{printf "%s\t%4.0f\t%4.0f\t%s\n" "chr"$1, $2-1, $2, $1":"$2"_"$3"_"$4}'
。但是,我收到以下错误消息:
awk: (FILENAME=- FNR=1) fatal: not enough arguments to satisfy format string
`%s %4.0f %4.0f %s
chr10'
^ ran out for this one
对于我的文件中的字段数量来说,格式字符串似乎太长,但我不确定这是为什么。在使用 printf 时,我发现了一个特点。如果我这样做awk '{printf "\t%s\t%4.0f\t%4.0f\t\n" "chr"$1, $2-1, $2, $1":"$2"_"$3"_"$4}'
,我会得到以下结果:
100000624 100000625 10
chr10 100000644 100000645 10
chr10 100002463 100002464 10
chr10 100003241 100003242 10
chr10 100003303 100003304 10
chr10 10000337 10000338 10
chr10 100003515 100003516 10
chr10 100003784 100003785 10
chr10 100004359 100004360 10
chr10 100004440 100004441 10
chr10
因此,第一列相对于所有其他列被下推一行,第四列被切断。如果在您的回复中,您还可以解释 printf 语法是如何工作的,我将非常感激。太感谢了!
答案1
您只是在格式字符串后面缺少一个逗号,例如
awk -F'[:_\t]' '{ printf "%s\t%4.0f\t%4.0f\t%s\n", "chr"$1, $2-1, $2, $1":"$2"_"$3"_"$4 }' file
# ^
# |
# this one
答案2
我根本无法重现您的问题,但假设它确实存在,这可能会帮助您:
$ cat tst.awk
BEGIN { OFS="\t"; OFMT="%d" }
{
split($1,f,/[:_]/)
print "chr"f[1], f[2]-1, f[2], $1
}
$ awk -f tst.awk file
chr10 100000624 100000625 10:100000625_A_G
chr10 100000644 100000645 10:100000645_A_C
chr10 100002463 100002464 10:100002464_C_T
chr10 100003241 100003242 10:100003242_G_T
chr10 100003303 100003304 10:100003304_A_G
chr10 10000337 10000338 10:10000338_C_T
chr10 100003515 100003516 10:100003516_A_G
chr10 100003784 100003785 10:100003785_C_T
chr10 100004359 100004360 10:100004360_A_G
chr10 100004440 100004441 10:100004441_C_G
如果%d
format 不能满足您的需要,但%4.0f
可以,则只需更改OFMT="%d"
为OFMT="%4.0f"
.
您提到的关于第一列被推下和第四列被截断的内容可能意味着您的输入中有 DOS 行结尾,请参阅https://stackoverflow.com/questions/45772525/why-does-my-tool-output-overwrite-itself-and-how-do-i-fix-it。
答案3
使用awk实用程序,我们可以这样做:
printf -v fmt '%s\t' '%s' '%4.0f' '%4.0f' '%s\n'
awk -F '\t' -v fmt="${fmt%?}" '
{
split($1, a, /[:_]/)
f1 = "chr" a[1]
f2 = (f3 = a[2])-1
f4 = sprintf("%4.0f", f3)
sub(/:[^_]+/, ":"f4, $1)
printf fmt, f1, f2, f3, $1
}
' file