我有一系列文件位于一系列文件夹中,例如:
~/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 特有的函数,类似于sub
和gsub
。它是手册中描述的sub
与和不同gsub
,gensub
回报变换后的值。