在系统调用open()
中,如果我用 打开O_CREAT | O_EXCL
,系统调用确保文件不存在时才会创建。原子性由系统调用保证。是否有类似的方法可以从 bash 脚本以原子方式创建文件?
更新:我发现了两种不同的原子方式
- 使用 set -o noclobber。然后您可以原子地使用 > 运算符。
- 只需使用 mkdir。mkdir 是原子的
100% 纯 bash 解决方案:
set -o noclobber
{ > file ; } &> /dev/null
file
如果不存在名为file
. 如果有一个名为 的文件file
,则什么也不做(但返回一个非零返回码)。
优点 wrttouch
命令:
file
已经存在或file
无法创建则失败;如果file
不存在并被创建则成功。缺点:
noclobber
选项(但在脚本中可以,如果您小心重定向,或者之后取消设置)。我猜这个解决方案实际上是open
系统调用的 bash 对应物O_CREAT | O_EXCL
。
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 脚本中以原子方式创建文件的最好方法遵循这种模式(而且它并不完美):
特别是,touch
它不是原子的,因为如果文件不存在,它将创建文件,或者只是更新时间戳。您也许可以玩不同时间戳的游戏,但读取和解析时间戳以查看您是否“赢得”比赛比上述更难。mkdir
可以是原子的,但您必须检查返回码,否则,您只能说“是的,目录已创建,但我不知道哪个线程获胜”。如果您使用的文件系统不支持硬链接,您可能不得不接受一个不太理想的解决方案。
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.
touch
是您要查找的命令。如果文件存在,它会更新所提供文件的时间戳,如果不存在则创建它。