在系统调用open()中,如果我用O_CREAT | O_EXCL打开文件,系统调用会确保只有当文件不存在时才会创建文件。原子性由系统调用保证。是否有类似的方法从bash脚本以原子方式创建文件?更新:我发现了两种不同的原子方式1.使用set -o noclobber,然后你就可以原子地使用〉操作符了。1.只需使用mkdir。Mkdir是原子的
open()
O_CREAT | O_EXCL
zzzyeukh1#
100%纯bash解决方案:
set -o noclobber { > file ; } &> /dev/null
如果不存在名为file的文件,此命令将创建一个名为file的文件;如果存在名为file的文件,则不执行任何操作(但返回一个非零返回代码)。>相对于touch命令的优点:
file
>
touch
缺点:
noclobber
我猜这个解决方案实际上是O_CREAT | O_EXCL的open系统调用的bash对应物。
open
mwkjh3gx2#
下面是一个使用mv -n技巧的bash函数:
mv -n
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
dxpyg8gm3#
你可以用一个随机生成的名字创建它,然后用你想要的名字重命名它(mv -n random desired)。如果文件已经存在,重命名将会失败。就像这样:
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
eulz3vhy4#
需要说明的是,确保文件只在不存在时才被创建与原子性不是一回事,当且仅当两个或多个单独的线程同时尝试做同一件事时,只有一个线程成功,其他线程都失败,操作才是原子的。据我所知,在shell脚本中原子地创建文件的最佳方法遵循以下模式(但它并不完美):1.创建一个极有可能不存在的文件(在文件名中使用一个合适的随机数选择或其他东西),并在其中放置一些唯一的内容(其他线程不会有的东西-同样,随机数或其他东西)1.验证文件是否存在,并且包含您期望的内容1.创建从该文件到所需文件的硬链接1.验证所需文件是否包含预期内容特别是,touch不是原子的,因为它会在文件不存在的情况下创建文件,或者只是更新时间戳。您可能可以使用不同的时间戳玩游戏,但阅读和解析时间戳以查看您是否“赢得”比赛比上面的操作更难。mkdir可以是原子的,但您必须检查返回代码,因为否则,你只能告诉“是的,目录被创建了,但是我不知道哪个线程赢了”。如果你在一个不支持硬链接的文件系统上,你可能不得不接受一个不太理想的解决方案。
mkdir
whhtz7ly5#
另一种方法是使用umask尝试创建文件并打开它进行写入,而不使用写权限创建它,如下所示:
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要求您对目录本身具有写权限,而不考虑文件权限。
exec
rm
dbf7pr2w6#
touch是您正在寻找的命令。如果文件存在,它将更新所提供文件的时间戳,如果文件不存在,它将创建该文件。
6条答案
按热度按时间zzzyeukh1#
100%纯bash解决方案:
如果不存在名为
file
的文件,此命令将创建一个名为file
的文件;如果存在名为file
的文件,则不执行任何操作(但返回一个非零返回代码)。>
相对于touch
命令的优点:file
已存在或无法创建file
,则失败;如果file
不存在且已创建,则成功。缺点:
noclobber
选项(但如果您小心使用重定向,或者在以后取消设置,那么在脚本中也可以)。我猜这个解决方案实际上是
O_CREAT | O_EXCL
的open
系统调用的bash对应物。mwkjh3gx2#
下面是一个使用
mv -n
技巧的bash函数:示例:
dxpyg8gm3#
你可以用一个随机生成的名字创建它,然后用你想要的名字重命名它(
mv -n random desired
)。如果文件已经存在,重命名将会失败。就像这样:
eulz3vhy4#
需要说明的是,确保文件只在不存在时才被创建与原子性不是一回事,当且仅当两个或多个单独的线程同时尝试做同一件事时,只有一个线程成功,其他线程都失败,操作才是原子的。
据我所知,在shell脚本中原子地创建文件的最佳方法遵循以下模式(但它并不完美):
1.创建一个极有可能不存在的文件(在文件名中使用一个合适的随机数选择或其他东西),并在其中放置一些唯一的内容(其他线程不会有的东西-同样,随机数或其他东西)
1.验证文件是否存在,并且包含您期望的内容
1.创建从该文件到所需文件的硬链接
1.验证所需文件是否包含预期内容
特别是,
touch
不是原子的,因为它会在文件不存在的情况下创建文件,或者只是更新时间戳。您可能可以使用不同的时间戳玩游戏,但阅读和解析时间戳以查看您是否“赢得”比赛比上面的操作更难。mkdir
可以是原子的,但您必须检查返回代码,因为否则,你只能告诉“是的,目录被创建了,但是我不知道哪个线程赢了”。如果你在一个不支持硬链接的文件系统上,你可能不得不接受一个不太理想的解决方案。whhtz7ly5#
另一种方法是使用
umask
尝试创建文件并打开它进行写入,而不使用写权限创建它,如下所示:如果文件丢失,脚本将成功创建并打开它进行写入,尽管文件是在没有写入权限的情况下创建的。如果文件已经存在,脚本将无法打开文件进行写入。可以使用
exec
打开文件并保留文件描述符。rm
要求您对目录本身具有写权限,而不考虑文件权限。dbf7pr2w6#
touch
是您正在寻找的命令。如果文件存在,它将更新所提供文件的时间戳,如果文件不存在,它将创建该文件。