使用 printf 和 A​​WK 正确格式化我的输出

使用 printf 和 A​​WK 正确格式化我的输出

我正在尝试使用 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

如果%dformat 不能满足您的需要,但%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

相关内容