检索制表符分隔文件的第一列和第五列,将第五列中的空格转换为制表符

检索制表符分隔文件的第一列和第五列,将第五列中的空格转换为制表符

我有一个tsv包含制表符分隔列的文件。我想获取第五列,其中有空格分隔的值。将空格分隔转换为制表符分隔并另存为新文件。

试图:

cut -d"\t" -f"4" input.tsv
awk -v OFS="\t" '$1=$1' input.tsv > output.tsv

输入:

Composite_Element_REF   Gene_Symbol     Chromosome      Genomic_Coordinate      TCGA-KL-8323-01A-21D-2312-05 TCGA-KL-8324-01A-11D-2312-05 TCGA-KL-8325-01A-11D-2312-05 
cg00000027      RBL0    14      53468110        0.0545368833399913 0.635089208882213 0.0581022991274144
cg00000028      RBL1    15      53468111        0.0545366588241415 0.635089205024173 0.0581085373336217
cg00000029      RBL2    16      53468112        0.0545366588040571 0.635089205078394 0.0581085373332275

预期输出:

Composite_Element_REF   TCGA-KL-8323-01A-21D-2312-05    TCGA-KL-8324-01A-11D-2312-05    TCGA-KL-8325-01A-11D-2312-05
cg00000027     0.0545368833399913    0.635089208882213    0.0581022991274144
cg00000028    0.0545366588241415    0.635089205024173    0.0581085373336217
cg00000029    0.0545366588040571    0.635089205078394    0.0581085373332275

答案1

如果您想要的是某个输入文件中的第一个和第五个制表符分隔字段input.tsv,然后将这些字段中的嵌入空格更改为制表符,您可以使用cut和执行此操作tr,如下所示:

cut -f 1,5 input.tsv | tr ' ' '\t' >output.tsv

首先从输入中提取第 1 个和第 5 个字段,然后将所有空格字符更改为制表符并将输出写入文件。如果第一个字段应该保持不变,则假定该字段不包含空格。

由于制表符是 的默认分隔符cut,因此我们不需要使用该-d选项。

或者,使用awk

awk 'BEGIN { OFS=FS="\t" }
    {
        nf = split($5, a, " ")
        $0 = $1

        for (i = 1; i <= nf; ++i) $(NF+1) = a[i]

        print
    }' input.tsv >output.tsv

这将按空格分割第五个制表符分隔字段,然后用第一个字段覆盖所有原始字段。在输出结果记录之前,将拆分字段添加到循环中的第一个字段之后。

通过 Miller( mlr),我们可以选择将输入读取为无头 TSV(无头,这样我们就简单地将头视为数据),剪掉我们感兴趣的字段,然后像我们一样重新创建每条记录做了awk,但更短:

mlr --tsv -N put '$* = {1:$1, 2:splita($5, " ")}' input.tsv >output.tsv

每个命令的输出将是

Composite_Element_REF   TCGA-KL-8323-01A-21D-2312-05    TCGA-KL-8324-01A-11D-2312-05    TCGA-KL-8325-01A-11D-2312-05
cg00000027      0.0545368833399913      0.635089208882213       0.0581022991274144
cg00000028      0.0545366588241415      0.635089205024173       0.0581085373336217
cg00000029      0.0545366588040571      0.635089205078394       0.0581085373332275

请注意,我假设您需要初始字段的标头,并且-05输入的第二行上的单独标头是一个拼写错误,应附加到标头行的最终字段的末尾。

答案2

使用awk

$ awk -F '\t' '{gsub(OFS,FS,$5); print $1 FS $5}' file

答案3

FWIW 实际上我会用@PrapjhotSingh 的解决方案但对于一些可能提供有关一般操作字段的有用信息的替代方案,请继续阅读...

要从输入中获取显示的输出,您需要显示的所有内容是:

$ awk -v OFS='\t' '{print $1, $5, $6, $7}' file
Composite_Element_REF   TCGA-KL-8323-01A-21D-2312-05    TCGA-KL-8324-01A-11D-2312-05    TCGA-KL-8325-01A-11D-2312-05
cg00000027      0.0545368833399913      0.635089208882213       0.0581022991274144
cg00000028      0.0545366588241415      0.635089205024173       0.0581085373336217
cg00000029      0.0545366588040571      0.635089205078394       0.0581085373332275

或者,因为我看到您尝试$1=$1在代码中使用,您可以这样做:

$ awk -v OFS='\t' '{$2=$3=$4=""; $0=$0; $1=$1} 1' file
Composite_Element_REF   TCGA-KL-8323-01A-21D-2312-05    TCGA-KL-8324-01A-11D-2312-05    TCGA-KL-8325-01A-11D-2312-05
cg00000027      0.0545368833399913      0.635089208882213       0.0581022991274144
cg00000028      0.0545366588241415      0.635089205024173       0.0581085373336217
cg00000029      0.0545366588040571      0.635089205078394       0.0581085373332275

在哪里:

  • $2=$3=$4=""将这些字段设置为 null 并重建,其中这些字段仍然存在但为空,用单个选项卡(我们正在使用的)$0替换所有空白和制表符(与我们使用的默认值匹配) ,因此原始中的空白变为制表符分为 3 个字段:、和。FSOFS$5$5$6$7
  • $0=$0重新拆分$0为字段,但不会更改字段之间的空白,因此这 3 个字段现已消失,但$1原始字段之间的多个选项卡$5(现在$2$3$4)仍然存在。
  • $1=$1设置$1为自身,再次$0从其字段进行重建,但现在多个连续的选项卡(再次与FS我们使用的默认选项匹配)被单个选项卡(我们正在使用的)替换,OFS导致最终输出 4 个选项卡分隔的字段。

NF您可以在下面看到这些不同的阶段,注意字段之间间距的变化以及每行开头打印的值的变化:

$ awk -v OFS='\t' '{print NF ": " $0}' < <(printf 'a\tb\tc\td\te f g\n')
7: a    b       c       d       e f g

$ awk -v OFS='\t' '{$2=$3=$4=""; print NF ": " $0}' < <(printf 'a\tb\tc\td\te f g\n')
7: a                            e       f       g

$ awk -v OFS='\t' '{$2=$3=$4=""; $0=$0; print NF ": " $0}' < <(printf 'a\tb\tc\td\te f g\n')
4: a                            e       f       g

$ awk -v OFS='\t' '{$2=$3=$4=""; $0=$0; $1=$1; print NF ": " $0}' < <(printf 'a\tb\tc\td\te f g\n')
4: a    e       f       g

或者,在某些 awk 中,例如 GNU awk,您可以执行以下任一操作:

awk -v OFS='\t' '{$2=$5; $3=$6; $4=$7; NF=4} 1' file
awk -v OFS='\t' '{for (i=2; i<=4; i++) $i=$(i+3); NF=i-1} 1' file

我们将 和 的值$5向上移动 3 个字段,然后设置NF为 4 以删除原始字段$5和后续字段,但设置NF是每个 POSIX 的未定义行为,因此不同的 awks 要么尊重该值,要么忽略该值,或者可以执行其他任何操作。

答案4

使用(以前称为 Perl_6)

~$ raku -ne 'put join "\t", .words.[0,4..*];'  file

上面是用 Raku(Perl 编程语言家族的成员)编写的答案。在这里我假设(与@Kusalananda相同)“输入的第二行上的唯一字符-05是一个拼写错误,应附加到标题行最后一个字段的末尾。”(来源:@Kusalananda)。

简而言之,使用非自动打印逐行标志逐行读取文件-ne。 Raku 的.words例程采用$_主题(输入行)并将其在空白处分解为元素。方括号[0,4..*]选择所需的零索引元素。这些在选项卡join上重新组合在一起\t,给出所需的输出。

输入示例:

Composite_Element_REF   Gene_Symbol     Chromosome      Genomic_Coordinate      TCGA-KL-8323-01A-21D-2312-05 TCGA-KL-8324-01A-11D-2312-05 TCGA-KL-8325-01A-11D-2312-05 
cg00000027      RBL0    14      53468110        0.0545368833399913 0.635089208882213 0.0581022991274144
cg00000028      RBL1    15      53468111        0.0545366588241415 0.635089205024173 0.0581085373336217
cg00000029      RBL2    16      53468112        0.0545366588040571 0.635089205078394 0.0581085373332275

示例输出:

Composite_Element_REF   TCGA-KL-8323-01A-21D-2312-05    TCGA-KL-8324-01A-11D-2312-05    TCGA-KL-8325-01A-11D-2312-05
cg00000027  0.0545368833399913  0.635089208882213   0.0581022991274144
cg00000028  0.0545366588241415  0.635089205024173   0.0581085373336217
cg00000029  0.0545366588040571  0.635089205078394   0.0581085373332275

处理“短”行相当容易,只需添加一个条件:

~$ raku -ne 'put join "\t", .words.[0,4..*] if .words.elems == 7;'  file

最后,如果您确实有一个包含 2 个不同分隔符的文件,您可以单独/连续管理它们:

~$ raku -ne '.split("\t", 5).[0,4].split(" ").join("\t").put;'   file

最后两个解决方案给出与上面相同的“示例输出”。

https://raku.org

相关内容