如果bash脚本中不存在c原子创建文件



在系统调用open()中,如果使用O_CREAT | O_EXCL打开,系统调用将确保仅在文件不存在的情况下创建该文件。原子性是由系统调用保证的。有没有类似的方法可以从bash脚本以原子方式创建文件?

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

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

100%纯bash解决方案:

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

如果不存在名为file的文件,则此命令将创建名为file的文件。如果有一个名为file的文件,则不执行任何操作(但返回一个非零的返回代码)。

>优于touch命令的优点:

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

缺点:

  • 需要设置noclobber选项(但在脚本中,如果您小心重定向或稍后取消设置,这是可以的)

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

下面是一个使用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

您可以用随机生成的名称创建它,然后用所需名称重命名(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

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

据我所知,在shell脚本中以原子方式创建文件的最佳方式如下(而且并不完美):

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

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

另一种方法是使用umask尝试创建文件并打开它进行写入,而无需使用写入权限创建它,如下所示:

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

如果文件丢失,脚本将成功创建并打开它进行写入,尽管创建的文件没有写入权限。如果它已经存在,脚本将无法打开文件进行写入。可以使用exec打开文件并保留文件描述符。

rm要求您拥有对目录本身的写入权限,而不考虑文件权限。

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

最新更新