首页 > 解决方案 > 防止两个或多个 awk 实例同时写入同一个文件

问题描述

awk 是否有任何内置支持来防止写入另一个 awk 实例已经写入的同一文件?

考虑以下:

$ # Create large input file
$ for i in {1..500000}; do echo "$i,$i,$i" >> /tmp/LargeFile.txt; done
$ # Launch two simultaneous instances of awk outputting to the same file
$ awk -F"," '{print $0}' /tmp/LargeFile.txt >> /tmp/OutputFile.txt & awk -F"," '{print $0}' /tmp/LargeFile.txt >> /tmp/OutputFile.txt &
$ # Find out how many fields are in each line (ideally 3)    
$ awk -F"," '{print NF}' /tmp/Output.txt | sort | uniq -c
          1 0
        553 1
       1282 2
     996412 3
       1114 4
        638 5

因此,两个 awk 实例同时将大量数据输出到同一个文件。理想情况下,输出文件的每行应包含三个逗号分隔值,但由于两个实例同时写入同一个文件,因此有些行可能有超过 3 个逗号分隔值,而有些则少于 3 个。

示例损坏的输出文件:

1,1,1   < 1's from from first instance of awk
2,2,2   < 2's from from first instance of awk
3,3,3   < 3's from from first instance of awk
1,1,1   < 1's from from second instance of awk
2,2,2   < 2's from from second instance of awk
4,4,4   < 4's from from first instance of awk
5,5,5   < 5's from from first instance of awk
3,3,3   < 3's from from second instance of awk
4,6,6,4,6   < corrupted input as both instances tried to write to this line at the same time
4
7,7,7   < 7's from from first instance of awk

有什么好的和简单的方法来防止这种情况吗?

编辑 - 从实际场景中添加更多细节:

每个 awk 实例所做的处理将更像这样:来自其他进程的数据被连续写入文件,例如每 5 分钟有一个新文件。将调用多个 awk 实例以按设定的时间间隔(例如每 30 分钟)处理/聚合数据。

cat SomeFilesWithLotsOfData | awk '
{
    # process lots of data which takes a lot of time
    # build up associate arrays based on input
}
END {
    # Output processed data which takes little time
    # Loop over associative arrays and output to persistent files
}'

假设处理部分(在 END 语句之前)需要 30 分钟才能完成(哇,那是很长的时间,但我们用它来说明一下)。可以实例化同一 awk 脚本的第二个实例,以在第一个实例结束之前处理带有数据的新批次文件,并且它需要将其处理后的数据输出到与前一个实例相同的文件中。每个 awk 实例输出到的输出文件的确切数量取决于输入(即,它基于输入记录中的特定字段)。我不想在处理输入之前锁定所有可能的输出文件,因为我不知道哪个 awk 实例将首先完成处理。所以目前我打算在 END 的开头创建一个锁并在 END 之后解锁它,但是我的实现有点笨拙,所以我

标签: fileawklocking

解决方案


在 Quick-and-dirty way 有一个类似的问题,以确保一次只运行一个 shell 脚本实例

flock(1)如果您的系统上存在该命令,则该解决方案可能是最简单的。

一种选择是简单地包装awk 脚本的所有调用:

flock -x /var/lock/myscriptlockfile awk ...

这将序列化 awk 脚本的调用,以便一次只能运行一个。如果您想决定是稍后重试还是跳过,您可以调整flock呼叫,使其在一段时间后终止,而不是永远等待。

要允许脚本的多个副本运行,但一次只允许一个副本,您可以调整此解决方案以flock从您的 END 中调用,以使用以下内容包装所谓的“关键部分”:

awk 300>/var/lock/myscriptlockfile '
    # ...
    END {
        system("flock -x 300");
        # critical section
        system("flock -u 300");
    }
'

推荐阅读