是否可以通过 GNU 并行将 awk 写入多个文件?

Posted

技术标签:

【中文标题】是否可以通过 GNU 并行将 awk 写入多个文件?【英文标题】:Is it possible to parallelize awk writing to multiple files through GNU parallel? 【发布时间】:2019-03-23 12:21:37 【问题描述】:

我正在运行一个 awk 脚本,我想通过 GNU 并行进行并行化。

此脚本根据每一行的值将一个输入文件多路分解为多个输出文件。代码如下:

#!/usr/bin/awk -f

BEGIN FS=OFS="\t" 

    # bc is the field that defines to which file the line
    # will be written
    bc = $1
    # append line to such file
    print >> (bc".txt")

我想通过以下方式使用 GNU 并行化它:

parallel --line-buffer --block 1G --pipe 'awk script.awk'

但是,我担心两个 awk 进程同时在同一个文件中写入可能出现的竞争条件。是否有可能,如果可以,如何在不影响并行化的情况下避免这种情况?

注意。我包含了--line-buffer 选项,尽管我不确定它是否也适用于 awk 脚本中的文件重定向。它是否也适用于这种情况或仅适用于每个 awk 进程的标准输出?

示例

# Input file
bc1    line1
bc3    line2
bc1    line3
bc2    line4


# Output file bc1.txt
bc1    line1
bc1    line3

# Output file bc2.txt
bc2    line4

# Output file bc3.txt
bc3    line2

【问题讨论】:

摆脱所有缓冲是很棘手的。让每个进程写入单独的文件并在之后将它们连接起来更安全。 如果这是可能的,我会感到惊讶。但也许你需要告诉我们为什么你认为你需要并行化它。只需让awk 运行一次,然后按照您在此处显示的内容进行编写。但是,如果您打开的文件超过*N*,则每次写入后都必须关闭。 (其中 N 是操作系统允许进程打开的文件数)。祝你好运! @karakfa 我现在正在测试的解决方案正是这样做的 - 但由于它需要更多操作,我想知道是否有现成的解决方案。 @shellter 关于打开文件数的好点。我需要并行化它,因为我有一个非常大的文件,我想将它拆分成块,因为单核 awk 目前是这里的瓶颈。 【参考方案1】:

您可以通过在不同的目录中解复用输出来做到这一点:

stuff |
  parallel --block 10M --pipe --round-robin \
    'mkdir -p dir-%; cd dir-%; awk ../script.awk'

或者如果输入是一个文件,你可以使用--pipepart,这样更快:

parallel --block -1 --pipepart -a bigfile \
  'mkdir -p dir-%; cd dir-%; awk ../script.awk'

那么就没有竞争条件了。通过合并目录完成:

parallel 'cd ; ls' ::: dir-* | sort -u |
  parallel 'cat */ > '

如果合并不可接受(可能您没有磁盘空间来存储 2 个数据副本),您可以使用 fifos。但要做到这一点,您需要提前知道所有.txt-文件的名称,并且您需要一个可以并行运行每个名称一个进程的系统(10000 个名称 = 10000 个进程):

# Generate names-of-files.txt somehow
# Make fifos for all names in all slots
parallel 'mkdir -p 2; mkfifo 2/1' :::: \
  names-of-files.txt <(seq $(parallel --number-of-threads) )
# Run the demultiplexer in the background
parallel --block -1 --pipepart -a bigfile \
  'mkdir -p dir-%; cd dir-%; awk ../script.awk' &
# Start one process per name
# If you have more than 32000 names, you will need to increase the number
# of processes on your system.
cat names-of-files.txt |
  parallel -j0 --pipe -N250 -I ,, parallel -j0 'parcat */ > '

【讨论】:

谢谢!附加:如果我只想在一组行上拆分文件(我可以交错行,但每一行都应该保持完整以便处理)我应该使用--line-buffer--pipepart 吗? 我真的不明白你的意思。但是--linebuffer 仅在输出来自parallel 时才有意义——而不是来自awk。提供3个输入输出示例。 我将示例添加到我的 OP 中。假设有 3 个并行作业,我希望每个作业处理 1 行或多行,但只处理整数行(不是行的一部分 - 比如说 line1 和 line2 的一部分)。希望这很清楚。 我的解决方案就是这样做的。你试过了吗? 是的,但是我对带有 pipepart 的块大小的概念感到困惑——我认为块大小是固定的,最后一条记录只包含了一部分。现在很清楚了,谢谢。

以上是关于是否可以通过 GNU 并行将 awk 写入多个文件?的主要内容,如果未能解决你的问题,请参考以下文章

Linux入门系列——awk命令详解

awk编程的基本用法

使用 AWK 处理来自多个文件的输入

逐行将字典写入纺织品[重复]

Windows CLI:将列表通过管道传输到 awk 并用外部文件中的文本替换文本并写入 output.txt

awk