如何在一个 awk 命令中替换 .tsv 文件中的列、重新排列、包含和排除数据

How to substitute columns, rearrange, include and exclude data in a .tsv file within one awk command

我有一个 .tsv 文件,我想在其中 1/ 替换第 6 列的值,直到最后一列,2/ 排除第 4 和第 5 列,3/ 重新排列,即将第 3 列替换为第 2 列,最后在第 2 和第 3 列之间包含一个值为数字 0 的列对于所有观察(线)。

1/ 从第 6 列开始的值可以是 0,1 或 2。我想做的是:如果数字是 0,用 1\t1 代替(这样它会创建另一列).如果数字为 1,则替换为 1\t2,如果为 2,则替换为 2/t2.

我设法通过生成一个新的 .tsv 来做到这一点,其中只有第 6 列以后的值,然后使用下面 awk 中的命令:

awk '{gsub(/2/,"2\t2");}1' file.txt > file1; awk '{gsub(/1/,"1\t2");}1' file1 > file2; awk '{gsub(/0/,"1\t1");}1' file2 > file3

之后我将处理 2/ 和 3/ 问题。我将使用 awk 从原始 .tsv 文件生成一个 .tsv 文件,该文件已经排除了第 4 和第 5 列,并使用以下命令交换了第 3 和第 2 列:

awk -v OFS="\t" '{ print , ,  }' original_tsv_file.tsv > reordered_tsv_file.tsv

然后,下一步是将数字 0 作为新生成的第 3 列包含在内 reordered_tsv_file.tsv。我使用了以下命令:

sed -i -e 's/^/0\t/' reordered_tsv_file.tsv

这将在每个新行前面包含数字 0,现在我只需执行之前步骤中的 awk 命令,对列重新排序,如下所示:

awk -v OFS="\t" '{ print , , ,  }' reordered_tsv_file.tsv > final_columns_to_be_merged_with_file3.tsv

最后,为了获得我想要的文件,我只需使用以下命令粘贴 final_columns_to_be_merged_with_file3.tsv 和 file3:

paste -d'\t' final_columns_to_be_merged_with_file3.tsv file3 > final_file.tsv

下面是 original_tsv_file.tsv 的示例:

chr15   101152646   chr15:101152646:A:G A   G   1   1   0   0
chr15   101152650   chr15:101152650:A:C A   C   1   1   0   0
chr15   101152872   chr15:101152872:G:A G   A   1   1   0   0
chr15   101152923   chr15:101152923:G:A G   A   1   1   0   0
chr15   101152954   chr15:101152954:C:T C   T   0   2   0   0
chr15   101153197   chr15:101153197:G:C G   C   0   2   0   0

和 final_file.tsv:

chr15   chr15:101152646:A:G 0   101152646   1   2   1   2   1   1   1   1
chr15   chr15:101152650:A:C 0   101152650   1   2   1   2   1   1   1   1
chr15   chr15:101152872:G:A 0   101152872   1   2   1   2   1   1   1   1
chr15   chr15:101152923:G:A 0   101152923   1   2   1   2   1   1   1   1
chr15   chr15:101152954:C:T 0   101152954   1   1   2   2   1   1   1   1
chr15   chr15:101153197:G:C 0   101153197   1   1   2   2   1   1   1   1

我想要的是关于如何在一段代码中完成此过程而不是使用 5 个不同的命令和大量生成的文件以获得最终结果的解决方案或建议。上面的所有命令我都设法从互联网上获取并修改,但我没有;还没有改进这一点的知识。

感谢您的帮助。

你可以试试

awk 'BEGIN{OFS="\t"}
     {printf "%s%s%s%s0%s%s", , OFS, , OFS, OFS, ; 
      for(i=6;i<=NF;++i){
          if($i==0) printf OFS"1"OFS"1"; 
          else if($i==1) printf OFS"1"OFS"2"; 
          else printf OFS"2"OFS"2"
      } printf ORS}
' original_tsv_file.tsv > final_file.tsv

你进了final_file.tsv

chr15   chr15:101152646:A:G 0   101152646   1   2   1   2   1   1   1   1
chr15   chr15:101152650:A:C 0   101152650   1   2   1   2   1   1   1   1
chr15   chr15:101152872:G:A 0   101152872   1   2   1   2   1   1   1   1
chr15   chr15:101152923:G:A 0   101152923   1   2   1   2   1   1   1   1
chr15   chr15:101152954:C:T 0   101152954   1   1   2   2   1   1   1   1
chr15   chr15:101153197:G:C 0   101153197   1   1   2   2   1   1   1   1

假设:

  • OP 希望所有输出列由制表符分隔 (\t)(预期输出似乎是固定宽度,但 OP 的代码提到 OFS="\t"
  • 第 6 列到 EOL 仅包含 3 个值之一:012

一个awk想法:

awk '
BEGIN { OFS="\t" }
      { outline=  OFS  OFS "0" OFS 
        for (i=6;i<=NF;i++)
                 if ($i==0) outline=outline OFS 1 OFS 1
            else if ($i==1) outline=outline OFS 1 OFS 2
            else            outline=outline OFS 2 OFS 2
        print outline
      }
' file.txt

使用几个三元运算的稍微复杂的想法:

awk '
BEGIN { OFS="\t" }
      { outline=  OFS  OFS "0" OFS 
        for (i=6;i<=NF;i++)
            outline=outline OFS \
                    (($i==0) ? 1 OFS 1 : (($i==1) ? 1 OFS 2 : 2 OFS 2))
        print outline
      }
' file.txt

这两个都会生成:

chr15   chr15:101152646:A:G     0       101152646       1       2       1       2       1       1       1    1
chr15   chr15:101152650:A:C     0       101152650       1       2       1       2       1       1       1    1
chr15   chr15:101152872:G:A     0       101152872       1       2       1       2       1       1       1    1
chr15   chr15:101152923:G:A     0       101152923       1       2       1       2       1       1       1    1
chr15   chr15:101152954:C:T     0       101152954       1       1       2       2       1       1       1    1
chr15   chr15:101153197:G:C     0       101153197       1       1       2       2       1       1       1    1

已经提到的第 4 列和第 5 列的要求将被丢弃,这使它们成为接收“0”和 </code> 值的额外列的完美位置。</p> <pre><code>f='sample1.tsv'; echo; cat "${f}" | ecp; echo; mawk2 'BEGIN { ___=length(FS="[ "(OFS="\t")"]+") ____=(__+=++__)+__ } { _=NF+=$____+= $__=substr("",($___=$__)~"") do { $_=(__-($_<__))"\t"(__-!$_) } while(___<--_) } gsub("\t+","\t")' "${f}" | ecp chr15 101152646 chr15:101152646:A:G A G 1 1 0 0 chr15 101152650 chr15:101152650:A:C A C 1 1 0 0 chr15 101152872 chr15:101152872:G:A G A 1 1 0 0 chr15 101152923 chr15:101152923:G:A G A 1 1 0 0 chr15 101152954 chr15:101152954:C:T C T 0 2 0 0 chr15 101153197 chr15:101153197:G:C G C 0 2 0 0 chr15 chr15:101152646:A:G 0 101152646 1 2 1 2 1 1 1 1 chr15 chr15:101152650:A:C 0 101152650 1 2 1 2 1 1 1 1 chr15 chr15:101152872:G:A 0 101152872 1 2 1 2 1 1 1 1 chr15 chr15:101152923:G:A 0 101152923 1 2 1 2 1 1 1 1 chr15 chr15:101152954:C:T 0 101152954 1 1 2 2 1 1 1 1 chr15 chr15:101153197:G:C 0 101153197 1 1 2 2 1 1 1 1

经测试适用于 gawk 5.1.1(模式 -e/Pe/ce)、mawk 1.3.4mawk 1.996macOS nawk

如果您非常确定输入只有制表符作为分隔符,那么它就更简单了:

gawk 'BEGIN{ ___=index(FS="["\
                     (OFS="\t")"]+","]")+(__+=++__) 
   } {  _=NF+=$(__+__)=+(\
              $__=substr($___=$__,_,_<_))
     do { $_=(__-($_<__) \
                  OFS (__-!$_) 
        } while(___<--_) } gsub(FS,OFS)'

— 4Chan 柜员