C语言 如果bash脚本中不存在原子创建文件

c3frrgcw  于 2023-02-18  发布在  其他
关注(0)|答案(6)|浏览(120)

在系统调用open()中,如果我用O_CREAT | O_EXCL打开文件,系统调用会确保只有当文件不存在时才会创建文件。原子性由系统调用保证。是否有类似的方法从bash脚本以原子方式创建文件?
更新:我发现了两种不同的原子方式
1.使用set -o noclobber,然后你就可以原子地使用〉操作符了。
1.只需使用mkdir。Mkdir是原子的

zzzyeukh

zzzyeukh1#

100%纯bash解决方案:

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

如果不存在名为file的文件,此命令将创建一个名为file的文件;如果存在名为file的文件,则不执行任何操作(但返回一个非零返回代码)。
>相对于touch命令的优点:

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

缺点:

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

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

mwkjh3gx

mwkjh3gx2#

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

dxpyg8gm3#

你可以用一个随机生成的名字创建它,然后用你想要的名字重命名它(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
eulz3vhy

eulz3vhy4#

需要说明的是,确保文件只在不存在时才被创建与原子性不是一回事,当且仅当两个或多个单独的线程同时尝试做同一件事时,只有一个线程成功,其他线程都失败,操作才是原子的。
据我所知,在shell脚本中原子地创建文件的最佳方法遵循以下模式(但它并不完美):
1.创建一个极有可能不存在的文件(在文件名中使用一个合适的随机数选择或其他东西),并在其中放置一些唯一的内容(其他线程不会有的东西-同样,随机数或其他东西)
1.验证文件是否存在,并且包含您期望的内容
1.创建从该文件到所需文件的硬链接
1.验证所需文件是否包含预期内容
特别是,touch不是原子的,因为它会在文件不存在的情况下创建文件,或者只是更新时间戳。您可能可以使用不同的时间戳玩游戏,但阅读和解析时间戳以查看您是否“赢得”比赛比上面的操作更难。mkdir可以是原子的,但您必须检查返回代码,因为否则,你只能告诉“是的,目录被创建了,但是我不知道哪个线程赢了”。如果你在一个不支持硬链接的文件系统上,你可能不得不接受一个不太理想的解决方案。

whhtz7ly

whhtz7ly5#

另一种方法是使用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要求您对目录本身具有写权限,而不考虑文件权限。

dbf7pr2w

dbf7pr2w6#

touch是您正在寻找的命令。如果文件存在,它将更新所提供文件的时间戳,如果文件不存在,它将创建该文件。

相关问题