使用For循环从一系列文件中提取某些列以写入新的制表符分隔文件

使用For循环从一系列文件中提取某些列以写入新的制表符分隔文件

我有一系列文件位于一系列文件夹中,例如:

~/BR2_1-3/bin.1.permissive.tsv
~/BR2_1-3/bin.2.permissive.tsv
~/BR2_1-3/bin.3.orig.tsv
~/BR2_2-4/bin.1.strict.tsv
~/BR2_2-4/bin.2.orig.tsv
~/BR2_2-4/bin.3.permissive.tsv
~/BR2_2-4/bin.4.permissive.tsv
~/BR2_3-5/bin.1.permissive.tsv
~/BR2_3-5/bin.2.permissive.tsv  
~/BR2_3-5/bin.3.orig.tsv        
~/BR2_3-5/bin.4.orig.tsv
~/BR2_3-5/bin.5.permissive.tsv
...

我想要做的是从每个 *.tsv 文件中提取第一列和第五列,并在相应的文件夹中创建一个新的制表符分隔文件。我可以使用以下命令分别对其相应文件夹下的每个文件执行此操作:

$ awk -F '\t' 'OFS="\t" {if ($5 != "") print($1,$5)}' bin.1.permissive.tsv > test
$ sed -i '1d' test
$ mv test BR2_1-bin.1.permissive.ec

我的问题是,因为我有一百多个此类文件,有没有办法编写一个for循环来自动在终端执行此步骤?

文件夹和文件的命名约定如下:文件夹为“BR(2~5)_(1~6)-(n,为文件夹中包含的文件数)”;文件的“bin.n.(strict/permissive/orig).tsv”。

一个输入文件应该映射到一个输出文件。如果相应的输入文件为“~/BR2_1-3/bin.1.permissive.tsv”,则输出文件的名称为“BR2_1-bin.1.permissive.ec”。如果相应的输入文件为“~/BR2_3-5/bin.3.orig.tsv”,则输出文件的名称为“BR2_3-bin.3.orig.ec”。此外,输出文件应与其相应的输入文件写入同一文件夹中。感谢评论中提出的这个问题。

预先感谢您,欢迎所有建议!

答案1

find通常xargs建议这样做:

find "$HOME" -name \*.tsv |
  xargs awk -F'\t' -v OFS='\t' '$5 != "" {print $1, $5}' >> output.tsv

或者,更安全地

find "$HOME" -name \*.tsv -print0 |
  xargs -0 awk -F'\t' -v OFS='\t' '$5 != "" {print $1, $5}' >> output.tsv

find 的-print0指令打印出用空字节分隔的匹配文件,xargs 的-0选项使用空字节分隔文件名。这样做是因为文件名中不允许出现空字节,而换行符是有效字符。


OK,对于每个文件要处理成对应的.ec文件:

find "$HOME" -name \*.tsv -print0 |
  xargs -0 awk -F '\t' -v OFS='\t' '
    FNR == 1 {
      if (ec) close(ec)
      ec = gensub(/\.tsv$/, ".ec", 1, FILENAME)
      next
    }
    $5 != "" {print $1, $5 > ec}
  '

笔记:

  • print ... > ex-- 与 shell 中的重定向类似,这会将输出重定向到变量中包含的文件名ec
    • 与 shell 不同,这不会为每个“打印”覆盖文件,但只有第一个打印会截断/创建文件,并且所有后续打印都会附加到该文件。
  • 您可能会遇到“打开的文件太多”错误,因此最好close在使用完文件后再打开它。
    • 当您位于文件的第一条记录时执行此操作
    • 如果ec变量不为空,则它保存用于以前的已处理的文件
  • gensub是 gawk 特有的函数,类似于subgsub。它是手册中描述的
    • sub与和不同gsubgensub 回报变换后的值。

相关内容