awk:命令。行:1:(FILENAME = file2split FNR = 1666)致命:无法重定向到“CCTGGCAG_GATATAAC_HAP1”(不允许操作)

awk:命令。行:1:(FILENAME = file2split FNR = 1666)致命:无法重定向到“CCTGGCAG_GATATAAC_HAP1”(不允许操作)

我想使用 ,根据文件的第一列值将文件拆分为多个文件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 个输出文件,并且不必多次打开输出文件,它只是打开它,写入全部关联到它的行,然后关闭它并移至下一个输出文件。

相关内容