【问题标题】:Loop through files in a directory and select rows based on column value using awk for large files遍历目录中的文件并使用 awk 根据列值选择大文件的行
【发布时间】:2022-08-19 07:01:28
【问题描述】:

我在一个文件夹中有 15 个文本文件(每个大约 1.5 - 2 GB),每个文件有大约 300,000 到 500,000 行和大约 250 列,每个都有一个带有列名的标题行。我还有一个包含五个值的列表(\"a123\"、\"b234\"、\"c345\"、\"d456\" 和 \"e567\")。 (这些是任意值,值不按顺序排列,彼此之间没有任何关系)

对于五个值中的每一个,我想在 15 个文本文件中的每一个中进行查询,如果 \"COL_ABC\" 或 \"COL_DEF\" 等于该值,则选择行。 (\"COL_ABC\" 和 \"COL_DEF\" 是任意名称,列名之间没有任何关系。)我不知道哪个列号是 \"COL_ABC\" 或 \"COL_DEF\"。它们在每个文件之间有所不同,因为每个文件都有不同的列数,但是在每个文件中 \"COL_ABC\"/\"COL_DEF\" 将被命名为 \"COL_ABC\"/\"COL_DEF\"。此外,一些文件同时具有 \"COL_ABC\" 和 \"COL_DEF\",但其他文件只有 \"COL_ABC\"。如果只有 \"COL_ABC\" 存在,我想对 \"COL_ABC\" 进行查询,但如果两者都存在,我想对两列进行查询(即检查 \"a123\" 是否存在于其他列中\"COL_ABC\" 或 \"COL_DEF\",如果为真则选择该行)。

我对 awk 很陌生,所以如果这是一个简单的问题,请原谅我。我只能进行简单的过滤,例如:

awk -F \"\\t\" \'{ if(($1 == \"1\") && ($2 == \"2\")) { print } }\' file1.txt

对于十五个文件中的每一个,我想将结果打印到一个新文件中。

通常我可以在 R 中执行此操作,但我的文件太大而无法读入 R。谢谢!

  • 这真的没有你想的那么糟糕。每个文件将按顺序处理,并且由于每个文件都包含一个标题行,只需使用\'FNR == 1 { ...; next } 作为规则中的条件即可读取每个文件中的第一条记录。循环遍历每个字段 for (i=1; i<=NF; i++) { col[$i] = i } 以按字段编号列 NAME 索引 col 数组。作为价值。然后对于其余的记录,您可以循环 for (i=1; i<=NF; i++) if (\"name_you_want\" in col) { # do what you want with $i value }。如果需要保留列顺序,可以交换 col[i] = $i
  • GNU Awk User\'s Guide 是你的朋友。

标签: awk


【解决方案1】:

假设:

  • 输入文件名的格式为“*.txt”。
  • 列由制表符分隔。
  • 五个值中的每一个都与目标列(COL_ABC 或 COL_DEF)一一进行比较 结果文件是根据值创建的。然后将创建 15 x 5 = 75 个文件。 (如果这不是你想要的,请告诉我。)

那你试试:

awk -F"\t" '
    BEGIN {
        values["a123"]                                  # assign values
        values["b234"]
        values["c345"]
        values["d456"]
        values["e567"]
    }
    FNR==1 {                                            # header line
        for (i in values) {                             # loop over values
            if (outfile[i] != "") close(outfile[i])     # close previous file
            outfile[i] = "result_" i "_" FILENAME       # filename to create
            print > outfile[i]                          # print the header
        }
        abc = def = 0                                   # reset the indexes
        for (i = 1; i <= NF; i++) {                     # loop over the column names
            if ($i == "COL_ABC") abc = i                # "COL_ABC" is found: assign abc to the index
            else if ($i == "COL_DEF") def = i           # "COL_DEF" is found: assign def to the index
        }
        next
    }
    {
        for (i in values) {
            if (abc > 0 && $abc == i || def > 0 && $def == i)
                print > outfile[i]                      # abc_th column or def_th column matches i
        }
    }
' *.txt

如果您的 15 个文本文件位于目录中,例如/path/to/the/dir/ 并且您想将目录指定为参数,请将最后一行中的 *.txt 更改为 /path/to/the/dir/*.txt

【讨论】:

  • 恕我直言,OP 没有要求输出文件(如果我没看错的话),所以你可以添加另一个只打印行的解决方案(因为 OP 的数据很大,它可以节省几个周期)?只是一个想法,谢谢分享好的答案。
  • @RavinderSingh13 感谢您的深思熟虑的评论。正如 OP 所说:For each of the fifteen files, I would like to print the results to a new file,我曾想过生成单个文件。可能太多了。我想等待OP的反馈。干杯!
  • 是的,你是对的@tshiono 制作像你显示的代码这样的文件会更好,这是个好方法。
  • 谢谢您的回答!我的实际值更像是“a123”、“f456”、d679”等,它们不是按顺序排列的,还包括字母而不是“1”、“2”、“3”、“4”、“5” 。您能否更新您的答案以反映这一点?我将编辑我的问题。是的,我想将结果打印到新文件中。
  • 感谢您的反馈。我已经相应地更新了我的答案(只需修改 BEGIN 块)。请你测试一下好吗? BR。
【解决方案2】:
for f in file*.txt; do
    awk -F'\t' '
        BEGIN {
            n1="COL_DEF"
            n2="COL_ABC"

            val["a123"] 
            val["b234"] 
            val["c345"] 
            val["d456"] 
            val["e567"] 
        }
        NR==1 {
            for(i=1; i<=NR; i++)
                col[$i]=i
            c=col[n1]
            if(!c) c=col[n2]
            next
        }
        $c in val { print }
    ' "$f" > "$f.new"
done
  • 我们真的不需要设置n1n2(我们可以直接使用字符串值)但是它将所有定义保存在一个地方
  • awk 没有很好的方法来一次声明整个数组的所有元素,因此我们单独设置 val 元素(或者,对于简单值 we could use split
  • 在文件的第一行(NR==1),我们存储标题名称,然后立即查找我们关心的并将索引存储在c中:我们选择col[n2]或@中的第一个987654330@ 被定义(非零)为要搜索的列索引
  • next 跳过此行的剩余 awk 操作
  • 然后对于剩余的每一行,我们检查相关列中的值是否是val 中的值之一,如果是,则打印该行。

awk 脚本包含在 bash for 循环中,我们根据循环变量将输出写入新文件。 (这都可以在 awk 本身中完成,但这种方式很容易。)

【讨论】:

  • 谢谢您的回答!我将第一行中的file*.txt 替换为*.txt,因为我的文件名不是以file 开头。在cd 进入包含所有文件的目录后,我只是将您的代码复制并粘贴到我的终端中。但是,不幸的是,我得到的新文件都是零字节。它们不应该是因为只需将其中一个文件加载到 R 中并运行我的查询就可以得到结果。
  • “我只是在复制和粘贴”对于您从互联网上获得的随机代码来说不是一个好主意。我看到你在我写完这个答案后改变了你的问题。希望很明显你可以用“a123”替换“1”等等,你自己?
  • 非常感谢,我自己可以用“a123”替换“1”。只是想提一下,我稍微修改了我的问题,所以现在你的答案可能会有所不同。 (有关其他答案,请参阅我最近的评论。谢谢!)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-04-01
  • 2016-09-14
  • 2013-05-14
  • 2013-09-21
相关资源
最近更新 更多