2012-12-11 51 views
5

在系统调用open(),如果我打开与O_CREAT | O_EXCL,系统调用确保该文件将被创建,如果它不存在。原子性由系统调用保证。有没有类似的方式从bash脚本以原子方式创建文件?原子创建文件,如果不存在bash脚本

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

  1. 使用设置-o noclobber选项。然后你可以自动使用>运算符。
  2. 只需使用mkdir即可。 Mkdir是原子的
+0

_The系统调用确保如果它不exist_哼文件才会被创建。如果文件不存在,它将被创建。如果存在,系统调用将失败。 – pbhd

+0

您可以尝试'mktemp'来创建一个文件,然后尝试将其“mv”到所需的名称。 –

回答

17

100%纯bash的解决方案:

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

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

优点WRT的touch命令:

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

缺点:

  • 需要设置noclobber选项(但没关系,在脚本中,如果你小心重定向,或取消其以后)。

我想这个解决方案真的是open系统调用与O_CREAT | O_EXCL的bash相对应。

+0

+1 - 短而干净。 –

+2

“>文件”保证是原子? –

+0

'(set -o noclobber;> file)&>/dev/null'也一样,但不会影响当前shell中的'noclobber'选项。 –

-2

touch是您正在查找的命令。它会更新所提供文件的时间戳,如果该文件存在,或者创建它,如果没有。

+3

除非文件已经存在,否则不会失败,正如O_EXCL所做的那样。 –

+0

我没有看过触摸源,但它可能不是原子的。 Otoh,如果文件存在,它会改变访问和修改时间。 –

+1

触摸不是原子 – Jimm

2

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

我所知道的在shell脚本原子创建一个文件的最佳方法遵循此模式(和它的不完美):

  1. 创建具有不存在极高的机会文件(使用体面的随机数字选择或文件名称中的某些内容),并在其中放置一些独特的内容(其他线程不会有的东西 - 再次,一个随机数或某物)
  2. 验证文件是否存在并包含您期望的内容它到
  3. 创建一个从该文件到所需文件的硬链接
  4. 验证所需的文件包含期望的内容

尤其touch不是原子,因为它会创建一个文件,如果它不存在,或者简单地更新时间戳。你可以用不同的时间戳来玩游戏,但是阅读和解析一个时间戳,看看你是否赢得了比赛比上面更难。 mkdir可以是原子的,但是你必须检查返回代码,否则,你只能说“是的,该目录是创建的,但我不知道哪个线程获胜”。如果您使用的是不支持硬链接的文件系统,那么您可能不得不选择不太理想的解决方案。

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 
+1

我的“mv”不是原子的。 它首先使用“stat”来测试目标文件是否存在,如果不存在,则使用“rename”。但是“重命名”只会覆盖目标。因此,在“统计”和“重命名”调用之间有一个很小的时间框架,其中用目标名称创建的文件将被覆盖。 – SIGSEGV

+0

啊,有趣。 'mv'根本没有帮助,那么,对不起。 –

3

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