17

在系统调用open()中,如果我用 打开O_CREAT | O_EXCL,系统调用确保文件不存在时才会创建。原子性由系统调用保证。是否有类似的方法可以从 bash 脚本以原子方式创建文件?

更新:我发现了两种不同的原子方式

  1. 使用 set -o noclobber。然后您可以原子地使用 > 运算符。
  2. 只需使用 mkdir。mkdir 是原子的
4

6 回答 6

28

100% 纯 bash 解决方案:

set -o noclobber
{ > file ; } &> /dev/null

file如果不存在名为file. 如果有一个名为 的文件file,则什么也不做(但返回一个非零返回码)。

优点 wrttouch命令:

  • 如果文件已存在,则不更新时间戳
  • 100% 内置 bash
  • 按预期返回代码:如果file已经存在或file无法创建则失败;如果file不存在并被创建则成功。

缺点:

  • 需要设置该noclobber选项(但在脚本中可以,如果您小心重定向,或者之后取消设置)。

我猜这个解决方案实际上是open系统调用的 bash 对应物O_CREAT | O_EXCL

于 2012-12-11T21:51:09.233 回答
5

mv -n这是一个使用该技巧的 bash 函数:

function mkatomic() {
  f="$(mktemp)"
  mv -n "$f" "$1"
  if [ -e "$f" ]; then
    rm "$f"
    echo "ERROR: file exists:" "$1" >&2
    return 1
  fi
}

例子:

$ mkatomic foo
$ wc -c foo
0 foo
$ mkatomic foo
ERROR: file exists: foo
于 2013-05-30T22:50:56.780 回答
3

您可以使用随机生成的名称创建它,然后将其重命名 ( mv -n random desired) 以使用所需的名称。如果文件已经存在,重命名将失败。

像这样:

#!/bin/bash

touch randomFileName
mv -n randomFileName lockFile

if [ -e randomFileName ] ; then
    echo "Failed to acquired lock"
else
    echo "Acquired lock"
fi
于 2012-12-11T21:32:53.960 回答
2

需要明确的是,确保仅在文件不存在时才创建文件与原子性不同。该操作是原子的当且仅当当两个或多个单独的线程尝试同时做同样的事情时,恰好一个会成功而所有其他的都会失败。

我所知道的在 shell 脚本中以原子方式创建文件的最好方法遵循这种模式(而且它并不完美):

  1. 创建一个极有可能不存在的文件(使用合适的随机数选择或文件名中的其他内容),并在其中放置一些独特的内容(其他线程不会有的内容 - 再次,随机数或其他内容)
  2. 验证文件是否存在并包含您期望的内容
  3. 创建从该文件到所需文件的硬链接
  4. 验证所需文件是否包含预期内容

特别是,touch它不是原子的,因为如果文件不存在,它将创建文件,或者只是更新时间戳。您也许可以玩不同时间戳的游戏,但读取和解析时间戳以查看您是否“赢得”比赛比上述更难。mkdir可以是原子的,但您必须检查返回码,否则,您只能说“是的,目录已创建,但我不知道哪个线程获胜”。如果您使用的文件系统不支持硬链接,您可能不得不接受一个不太理想的解决方案。

于 2012-12-11T21:30:54.177 回答
0

Another way to do this is to use umask to try to create the file and open it for writing, without creating it with write permissions, like this:

LOCK_FILE=only_one_at_a_time_please
UMASK=$(umask)
umask 777
echo "$$" > "$LOCK_FILE"
umask "$UMASK"
trap "rm '$LOCK_FILE'" EXIT

If the file is missing, the script will succeed at creating and opening it for writing, despite the file being created without writing permissions. If it already exists, the script won't be able to open the file for writing. It would be possible to use exec to open the file and keep the file descriptor around.

rm requires you to have write permissions to the directory itself, without regards to file permissions.

于 2020-06-02T22:17:19.807 回答
-3

touch是您要查找的命令。如果文件存在,它会更新所提供文件的时间戳,如果不存在则创建它。

于 2012-12-11T21:13:53.133 回答