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

时间:2012-12-11 21:09:30

标签: c linux bash

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

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

  1. 使用set -o noclobber。然后你可以使用>原子地运营商。
  2. 只需使用mkdir。 Mkdir是原子的

6 个答案:

答案 0 :(得分:21)

100%纯bash溶液:

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

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

专业人员touch命令:

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

缺点:

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

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

答案 1 :(得分: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

答案 2 :(得分:2)

为了清楚起见,确保仅在不存在的情况下创建文件与原子性不同。当且仅当两个或多个单独的线程同时尝试执行相同的操作时,该操作才是原子操作,只有一个将成功,而其他所有线程都将失败。

我知道在shell脚本中以原子方式创建文件的最佳方式遵循此模式(并且它并不完美):

  1. 创建一个极有可能不存在的文件(使用正确的随机数选择或文件名中的某些内容),并在其中放置一些独特的内容(没有其他线程会有的东西 - 再次,随机数字或其他)
  2. 验证文件是否存在并包含您希望它的内容
  3. 创建从该文件到所需文件的硬链接
  4. 验证所需文件是否包含预期内容
  5. 特别是,touch不是原子的,因为如果文件不存在,它将创建文件,或者只是更新时间戳。您可能能够玩具有不同时间戳的游戏,但是阅读和解析时间戳以查看您是否“赢得”比赛比上述更难。 mkdir可以是原子的,但是您必须检查返回代码,否则,您只能告诉“是的,目录已创建,但我不知道哪个线程赢了”。如果您使用的文件系统不支持硬链接,则可能需要采用不太理想的解决方案。

答案 3 :(得分:2)

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

答案 4 :(得分:0)

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

答案 5 :(得分:-3)

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