如何根据原始文件的第一列值组织新文件?

时间:2016-02-12 22:50:24

标签: linux bash sorting awk

假设我有3个文件:File-AFile-BFile-C;其中每个文件有两列数据(空格描绘)但行数未知(和可变)。

输入

File-A:
1 dE
1 dF         
2 dF
2 dH         

File-B:    
1 dI         
3 dJ
3 dK         

File-C:
2 dF
3 dH
3 dJ
3 dK
4 dL

如何有效地对数据进行排序,以便为第一列中的每个值创建新文件(即File-1File-2File-3File-4)跟踪他们的第二列合作伙伴数据和原始文件名?

所需输出

File-1:      
A dE
A dF
B dI

File-2:    
A dF
A dH         
C dF         

File-3:
B dJ
B dK
C dH
C dJ
C dK

File4:
C dL

实际上,我有几十万个原始文件,每个文件都有几百行数据(但原始文件和新文件的总数是已知的)。什么是实现这种类型排序的最有效方法?

Bash脚本是否是最快的方法而不是像Fortran这样的程序?我只是学习sed和awk - 这样的工作效果最好吗?

如果在链接受到赞赏之前提出了类似的问题。到目前为止我发现的closest question似乎表明awk可能是一种可行的方式。

1 个答案:

答案 0 :(得分:2)

这是一个(可能很慢)Bash解决方案:

#!/bin/bash

for suffix in "${@##*-}"; do    # Get suffix from each file name
    while read -r col1 col2; do # Read two columns

        # Assemble output line and write to proper file
        printf "%s %s\n" "$suffix" "$col2" >> "File-$col1"
    done < "File-$suffix"
done

Bash循环很慢,许多重定向都很慢,但我无法想到另一种方法,因为每个输入行都可能转到另一个输出文件。

awk中可能更快的东西:

#!/usr/bin/awk -f

# For each new file, get the file name suffix
FNR == 1 {
    split(FILENAME, arr, "-")
    suffix = arr[2]
}

# On each line, create the output file name, then print to that file
{
    ofname = "File-" $1
    print suffix, $2  > ofname
}

使用./scriptname File-*从命令行调用两者。

限制打开文件句柄的数量

可以同时打开多少个文件句柄是有限制的:来自您的操作系统和来自awk。 Gawk做了一些诡计 1 来解决这个问题,但是为了避免打开过多的文件句柄,它可能仍然会更快(并且更加便携)。

例如,一种补救方法是跟踪每个输入文件的打开文件句柄,然后在处理下一个文件之前关闭它们:

#!/usr/bin/awk -f

# For each new file, get the file name suffix
FNR == 1 {
    # Close open files
    for (fname in openfiles)
        close(openfiles[fname])
    split(FILENAME, arr, "-")
    suffix = arr[2]
}

# On each line, create the output file name, then print to that file
{
    ofname = "File-" $1
    openfiles[ofname] = 1   # Keep track of open files
    print suffix, $2  > ofname
}

1 来自manual

  

如果您使用的文件多于系统允许您打开的文件,gawk会尝试在您的数据文件中复用可用的打开文件。 gawk执行此操作的能力取决于操作系统的功能,因此可能并不总是有效。

     

因此,在完成文件后,始终对文件使用close()是一种良好的做法和良好的可移植性建议。实际上,如果您使用大量管道,则必须在完成后关闭命令。