按第一列拆分文件:打开的文件太多

按第一列拆分文件:打开的文件太多

这是问题的延伸:根据第一列值将一个文件拆分为多个文件。由于我刚刚加入,我没有足够的代表点来对上述问题添加评论。因此,我对重复表示歉意。

我使用以下命令按第一列拆分文件:

awk -F"\t" '{print>"subfolder/"$1}' inputfile

但是,我得到了一个awk: cannot open for output (Too many open files).

然后我将命令更改为以下内容

awk -F"\t" '{print>"subfolder/"$1}{close("subfolder/"$1)}' inputfile

但是,关闭文件会生成 0 字节的输出文件。

答案1

使用print >filenameinawk将打开文件并将其长度截断为零(如果存在)。 awk那么会保持文件打开直到程序结束。如果您对许多文件执行此操作,您将遇到资源限制,正如您所注意到的。

您需要做的是close(filename).就你而言close("subfolder/"$1)。您需要在$1仍然具有正确值的情况下执行此操作。

然而,这意味着,那么下一个 print >到该文件将打开该文件并截断​​其先前的内容。

要解决此问题,请使用print >>而不是print >.这将打开文件追加

下一个问题是,如果您awk第二次运行程序,第一次运行的结果将被附加到后面。这意味着您必须在再次运行程序之前组织要删除或重命名的输出文件。

完整的脚本可能看起来像这样

#!/bin/sh

rm -rf subfolder   # remove old output files 
mkdir subfolder    # and recreate output directory

awk -F '\t' '{ fname = "subfolder/" $1; print >>fname; close(fname) }' inputfile

如果您的数据在第一列上排序,那么您可能会受益于一个非常小的优化,那就是在您实际需要之前不要关闭文件:

awk -F '\t' '
    fname != "subfolder/" $1 {
        if (fname != "")
            close(fname)
        fname = "subfolder/" $1
    }
    { print >>fname }' inputfile

如果输入确实在第一个字段上排序,那么您可以更改print >>print >上面的。即使您的数据未排序,此(使用print >>)也会将恰好具有相同第一个字段的多个连续行写入同一文件,而无需关闭并重新打开中间的输出文件(这可能会很慢)。


正如 mosvy 在评论中指出的那样,您可能很想确保文件名使用的值是理智的在盲目地写它之前。

您可以通过显式检查该值是否仅包含小写或大写字母数字字符(和下划线)来做到这一点:

awk -F '\t' '
    fname != "subfolder/" $1 {
        if (fname != "")
            close(fname)
        fname = "subfolder/" $1

        if (fname ~ /[^a-zA-Z0-9_]/) {
            print "Bad filename: " fname >"/dev/stderr"
            exit(1)
        }
    }
    { print >>fname }' inputfile

他还建议了一种处理重定向到输出文件的替代方法,该方法会在第一次打开文件时截断文件,但会在其他任何时候打开它以进行追加。他通过将文件名保存为关联哈希中的键来实现这一点:

    {
        if (names[fname]++)
            print >>fname
         else
            print >fname
    }

相关内容