使用awk
, 在此表中,我想添加一列,其中第一行是“INFO”,其余行都是“1”。
$ gunzip -c foo.gz | head
SNPID CHR BP Allele1 Allele2 Freq1 Effect StdErr P.value TotalN
rs1000033 1 226580387 t g 0.8266 -0.0574 0.0348 0.09867 17310
rs1000050 1 162736463 t c 0.8545 0.0654 0.0461 0.1564 10864
在哪里
gunzip -c foo.gz | head | cat -A
SNPID^ICHR^IBP^IAllele1^IAllele2^IFreq1^IEffect^IStdErr^IP.value^ITotalN^M$
rs1000033^I1^I226580387^It^Ig^I0.8266^I-0.0574^I0.0348^I0.09867^I17310^M$
rs1000050^I1^I162736463^It^Ic^I0.8545^I0.0654^I0.0461^I0.1564^I10864^M$
因为这是.gz
我使用过的文件
gunzip -c foo.gz | \
awk 'BEGIN {FS="\t"; OFS="\t"} NR == 1 {print $0 OFS "INFO"} NR > 1 {print $0 OFS "1"}' | \
gzip > foo.V2.gz
由于某种原因,这似乎改变了我的列名,但没有改变最后的预期列。
$ gunzip -c foo.V2.gz | head
SNPID INFO BP Allele1 Allele2 Freq1 Effect StdErr P.value TotalN
--------^
rs1000031 1 226580387 t g 0.8266 -0.0574 0.0348 0.09867 17310
rs1000051 1 162736463 t c 0.8545 0.0654 0.0461 0.1564 10864
奇怪的是,当我cat -A
查看该列时,该列似乎位于其应在的位置。
$ gunzip -c foo.V2.gz | head | cat -A
SNPID^ICHR^IBP^IAllele1^IAllele2^IFreq1^IEffect^IStdErr^IP.value^ITotalN^M^IINFO$
----------------------------------------------------------------------------^
rs1000033^I1^I226580387^It^Ig^I0.8266^I-0.0574^I0.0348^I0.09867^I17310^M^I1$
rs1000050^I1^I162736463^It^Ic^I0.8545^I0.0654^I0.0461^I0.1564^I10864^M^I1$
我想知道,
- 这里发生了什么事?
- 我可以相信
gunzip -c foo.V2.gz | head
还是gunzip -c foo.V2.gz | head | cat -A
现在? - 如何使用获得我的预期输出
gunzip -c foo.V2.gz | head
SNPID CHR BP Allele1 Allele2 Freq1 Effect StdErr P.value TotalN INFO
rs1000033 1 226580387 t g 0.8266 -0.0574 0.0348 0.09867 17310 1
rs1000050 1 162736463 t c 0.8545 0.0654 0.0461 0.1564 10864 1
注意,我正在使用一个配置脚本来定义SNPID=1; CHR=2; ...
我所在的位置,具体取决于我指定的列号对于后续分析是否正确。
正如已经提到的,你有 DOS 行结尾。请参阅Why-does-my-tool-output-overwrite-itself-and-how-do-i-fix-it了解问题的描述和可能的解决方案,例如使用任何 awk:
您可以使用
RS="\r\n"
多字符 RS,它是一个 GNU awk 扩展,最近被 1 或 2 个其他 awk 变体采用。对于任何其他符合 POSIX 的 awk 设置,RS="\r\n"
将被视为与您设置的设置相同RS="\r"
,因为每个 POSIXRS
只能是单个文字字符。\r
在底层C 原语在 awk 看到行尾之前将其剥离的系统上,它也会失败,因此RS="\r?\n"
更加健壮。对于任何 awk,您都可以保留RS
其默认值\n
并添加{sub(\r$/,"")}
为脚本的第一条语句。我还整理了脚本中的其他一些内容,例如删除不需要或已经具有该值的代码设置变量,将 2 个打印语句更改为 1 个,按设计使用 OFS,并消除不必要的转义在管道符号之后的行尾。
您的输入似乎是某种带有 Microsoft 行结尾的 TSV 文件。
然后您可以使用支持 2 种 tsv 并可以指定记录分隔符的
mlr
代替。awk
--tsv
v
值是t
abs
分隔的,但\\
,\t
,\r
,\n
可用于\
在字段中嵌入 , TAB, CR 和 LF 字符。--tsvlite
v
是t
abs
分隔的,并且不可能在字段值中嵌入行分隔符或制表符。在这里,由于您只想添加一个额外的列,其标题和值均不包含任何这些字符,因此使用其中之一不会产生任何影响。
默认情况下,
mlr
接受 CRLF (Microsoft) 或 LF (Unix) 行分隔符并输出用 LF (Unix) 分隔的行。但您可以--rs crlf
将其传递给以 CRLF (Microsoft) 分隔的输出行。所以:
在输出上获取 Unix tsv 并在输入上接受 Microsoft 或 Unix tsv。
在输出上获取 Microsoft tsv 并在输入上接受 Microsoft 或 Unix tsv。
从6.0.0版本开始,
mlr
内置了读取gzip压缩文件的支持,所以你还可以这样做:(
--gzin
如果文件路径不以 结尾,则传递该选项.gz
)。mlr
(miller,一般打包发货miller
)是专门处理表格数据的工具。它可以使用多个动词作为对记录执行不同操作的参数,例如sort
,cut
,join
,filter
...put
是用于使用一种简单的特定于域的语言对记录进行修改的语言,与awk
.在该语言中,如 in
awk
,$
用于引用记录中的字段,但这些字段名称为¹。对于$INFO = 1
,我们为每条记录INFO
的字段赋予一个 numeric² 值1
。如果该字段尚不存在,则会添加该字段并将其显示为额外列。1 不过也可以像使用
--implicit-csv-header
.² 您可以将
$INFO = "1"
其设置为字符串,这会对 JSON 等输出格式产生不同的影响,但对于没有类型指示的 tsv 则不然。正如@steeldriver在评论中指出的那样,该文件似乎是 Windows 污染了回车 (CR) 和换行 (LF) 字符,我们可以删除设置记录分隔符,
RS="\r\n"
但ORS="\n"
不会再次引入相同的问题。要在行分隔符之前添加
<tab>INFO
第一行或<tab>1
后续行,无论该行分隔符是 LF (Unix) 还是 CR 后跟 LF (Microsoft),您可以执行以下操作:要就地编辑文件,理论上您应该能够
-i
与PerlIO::gzip
IO 层一起使用该选项(可能需要单独安装):虽然我发现我的版本失败了,并出现Can't do inplace edit on foo.gz: Cannot make temp name: Inproperty ioctl for device。这对我来说听起来像是一个错误。