原子地删除文件的第一行并返回它

atomically removing the first line of a file AND returning it

我想使用纯文本文件作为我的应用程序的任务队列(用于写入数据库,尽管这无关紧要),其中:

  1. 我通过 echo "some task" >> task_queue.txt
  2. 将项目添加到队列
  3. 我从队列中自动删除了一个项目

对于 2。如果我在多个线程或进程中 accessing/modifying task_queue.txt,我不知道如何避免竞争条件。以下不是原子的:

ITEM=`head -1 task_queue.txt`
sed -i '1d' task_queue.txt
# process the item in the application

Bash 是否提供了比使用锁定文件更优雅的方式来执行此操作?我以前从未使用过 flock,所以我不知道这是否很混乱(例如,当我的应用程序的任务处理失败时)。

在检查文件是否被访问和对文件进行操作之间,所有这些都存在竞争条件。虽然大概检查和操作之间的时间会很短。


一种方法是使用 pgrep-f 选项来匹配完整的命令行,并匹配文件是否匹配,即是否有任何进程访问该文件。这假设进程没有修改它的命令行。

这可以做到:

if ! pgrep -f task_queue.txt &>/dev/null; then
    ## File not Open, do stuff
else
    ## File is Open, do stuff
fi

另一种方法包括解析 lsoffuser(这与解析 /proc/PID/fd/* 相同):

if ! lsof /path/to/task_queue.txt &>/dev/null; then
    ## File not Open, do stuff
else
    ## File is Open, do stuff
fi

同样fuser:

if ! fuser /path/to/task_queue.txt &>/dev/null; then
    ## File not Open, do stuff
else
    ## File is Open, do stuff
fi

请注意,这里我们将 lsof/fuser 的 STDOUT 和 STDERR 发送到 /dev/null,这可能并不总是可取的,因为可能有一些 warning/error,因为我们只依赖于退出状态,所以所有这些都将被误认为文件正在使用中。如果 lsof/fuser 对不同的事件有不同的退出状态,这将更容易实现,但我能看到的是 1 对于每种失败或不匹配。

虽然我无法确认它是原子的,但这比我自己写的任何东西(可能)都更原子,并且对于我的非关键任务应用程序来说已经足够好了:

sed -i -e '1 w /dev/stdout' -e '1d' task_queue.txt

(学分:https://unix.stackexchange.com/a/108479/7000