我想使用 ,根据文件的第一列值将文件拆分为多个文件zcat file2split.gz | awk '{print>$1}'
,但遇到以下错误:
awk: cmd. line:1: (FILENAME=file2split FNR=1666) fatal: can't redirect to `CCTGGCAG_GATATAAC_HAP1' (Operation not permitted)
有什么想法吗?谢谢!
zip 数据大小为 25Mb,可以在此处下载: https://drive.google.com/file/d/1Qjq-ibdiyemBfuqpoC2h0VDhw09PS0ao/view?usp=sharing
答案1
每当当前输出文件名更改时,您可以通过关闭以前的文件名来避免遇到打开文件限制。例如
awk '{ out=$1;
if (out != lastfile) {
if (lastfile != "") { close(lastfile) };
lastfile = out
};
print > out'
这比关闭文件并重新打开它要好得多每一个写。仅当文件名自上次写入以来发生更改时,它才会关闭文件。如果文件碰巧按字段 1 排序,则它永远不需要重新打开文件...并且如果它是“大部分排序”,则很少需要重新打开文件。
注意:如果相同的文件名可能出现多次不相邻行,那么您应该附加输出print >> out
而不是print > out
,否则当文件是重新开放对于写入,它将在写入之前被完全覆盖(即截断为零大小)。
(顺便说一句,这是每次写入后不会自动关闭文件的原因之一awk
。当然,另一个主要原因是,如果不这样做,每次写入时关闭并重新打开相同的文件会慢得多。不必)
如果您想在 awk 脚本的任何给定运行中第一次看到该文件时覆盖该文件,那么这只会稍微复杂一些,但如果在同一运行中再次看到相同的文件则追加。例如
awk '{ out=$1;
if (out != lastfile) {
if (lastfile != "") { close(lastfile) };
lastfile = out
};
if (seen[out]++) {
print >> out
} else {
print > out
}'
这个版本使用关联数组seen
来跟踪我们之前是否见过某个文件名。如果有,请追加。如果没有,则覆盖。
答案2
我怀疑太多同时打开的文件是否确实导致了您当前的问题,但仅供参考,执行您想要执行的操作的强大、有效的方法是使用 GNU sort for-s
和 any awk 进行以下操作:
zcat file2split.gz |
sort -s -k1,1 |
awk '
$1 != out {
close(out)
out = $1
}
{ print > out }
'
如果您没有 GNU 排序,您可以使用这些标准 Unix 工具的任何版本执行相同的操作:
zcat file2split.gz |
awk -v OFS='\t' '{print NR, $0}' |
sort -k2,2 -k1,1n |
cut -f2- |
awk '
$1 != out {
close(out)
out = $1
}
{ print > out }
'
通过上述在 awk 开始创建输出文件之前对输入进行排序的方法,awk 一次只打开 1 个输出文件,并且不必多次打开输出文件,它只是打开它,写入全部关联到它的行,然后关闭它并移至下一个输出文件。