bash 原子地移动目录

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/307437/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-17 20:32:24  来源:igfitidea点击:

Moving a directory atomically

linuxbashatomic

提问by dirtside

I have two directories in the same parent directory. Call the parent directory baseand the children directories alphaand bravo. I want to replace alphawith bravo. The simplest method is:

我在同一个父目录中有两个目录。调用父目录和子目录alphabravo。我想用bravo替换alpha。最简单的方法是:

rm -rf alpha
mv bravo alpha

The mv command is atomic, but the rm -rf is not. Is there a simple way in bash to atomically replace alphawith bravo? If not, is there a complicated way?

mv 命令是原子的,但 rm -rf 不是。bash 中是否有一种简单的方法可以用bravo原子地替换alpha?如果没有,有没有复杂的方法?

ADDENDUM:

附录:

By the by, it's not an insurmountable problem if the directory doesn't exist for a short period. There's only one place that tries to access alpha, and it checks if alpha exists before doing anything critical. If not, it gives an error message. But it would be nice if there was a way to do this. :) Maybe there's some way to modify the inodes directly, or something...

顺便说一句,如果目录在短时间内不存在,这不是一个无法解决的问题。只有一个地方会尝试访问 alpha,它会在执行任何关键操作之前检查 alpha 是否存在。如果没有,它会给出错误消息。但如果有办法做到这一点就好了。:) 也许有一些方法可以直接修改 inode,或者其他什么......

采纳答案by Doug Currie

You can do this if you use symlinks:

如果您使用符号链接,您可以这样做:

Let's say alpha is a symlink to directory alpha_1, and you want to switch the symlink to point to alpha_2. Here's what that looks like before the switch:

假设 alpha 是指向目录 alpha_1 的符号链接,并且您希望将符号链接切换为指向 alpha_2。这是切换前的样子:

$ ls -l
lrwxrwxrwx alpha -> alpha_1
drwxr-xr-x alpha_1
drwxr-xr-x alpha_2

To make alpha refer to alpha_2, use ln -nsf:

要使 alpha 引用 alpha_2,请使用 ln -nsf:

$ ln -nsf alpha_2 alpha
$ ls -l
lrwxrwxrwx alpha -> alpha_2
drwxr-xr-x alpha_1
drwxr-xr-x alpha_2

Now you can remove the old directory:

现在您可以删除旧目录:

$ rm -rf alpha_1

Note that this is NOT actually a fully atomic operation, but it does happen very quickly since the "ln" command both unlinks and then immediately recreates the symlink. You can verify this behaviour with strace:

请注意,这实际上不是一个完全原子的操作,但它确实发生得非常快,因为“ln”命令既取消链接又立即重新创建符号链接。您可以使用 strace 验证此行为:

$ strace ln -nsf alpha_2 alpha
...
symlink("alpha_2", "alpha")             = -1 EEXIST (File exists)
unlink("alpha")                         = 0
symlink("alpha_2", "alpha")             = 0
...

You can repeat this procedure as desired: e.g. when you have a new version, alpha_3:

您可以根据需要重复此过程:例如,当您有新版本 alpha_3 时:

$ ln -nsf alpha_3 alpha
$ rm -rf alpha_2

回答by David Schmitt

The final solution is combining the symlink- and the rename-approach:

最终的解决方案是结合符号链接和重命名方法:

mkdir alpha_real
ln -s alpha_real alpha

# now use "alpha"

mkdir beta_real
ln -s beta_real tmp 

# atomically rename "tmp" to "alpha"
# use -T to actually replace "alpha" instead of moving *into* "alpha"
mv -T tmp alpha

Of course, the application accessing alpha has to be able to deal with symlinks changing in the path.

当然,访问 alpha 的应用程序必须能够处理路径中的符号链接更改。

回答by mssaxm

Picking up on David's solution here, which is fully atomic ... the only problem you'd run into is that the -Toption for mvis non-POSIX, and so certain POSIX OSes may not support it (FreeBSD, Solaris, etc. ... http://pubs.opengroup.org/onlinepubs/9699919799/utilities/mv.html). With slight modification, this approach can be altered to be fully atomic, and portable across all POSIX OSes:

在这里选择 David 的解决方案,这是完全原子的……您遇到的唯一问题是该-T选项mv是非 POSIX,因此某些 POSIX 操作系统可能不支持它(FreeBSD、Solaris 等... . http://pubs.opengroup.org/onlinepubs/9699919799/utilities/mv.html)。只需稍加修改,就可以将这种方法更改为完全原子的,并且可以在所有 POSIX 操作系统中移植:

mkdir -p tmp/real_dir1 tmp/real_dir2
touch tmp/real_dir1/a tmp/real_dir2/a
# start with ./target_dir pointing to tmp/real_dir1
ln -s tmp/real_dir1 target_dir
# create a symlink named target_dir in tmp, pointing to real_dir2
ln -sf tmp/real_dir2 tmp/target_dir
# atomically mv it into ./ replacing ./target_dir
mv tmp/target_dir ./

exaple via: http://axialcorps.wordpress.com/2013/07/03/atomically-replacing-files-and-directories/

例如通过:http: //axiscorps.wordpress.com/2013/07/03/atomically-replacing-files-and-directories/

回答by Lucas Werkmeister

Since Linux 3.15, the new renameat2system call can atomically exchange two paths on the same file system. However, there's not even a glibc wrapper for it yet, let alone a coreutils way to access it. So it would look something like this:

从 Linux 3.15 开始,新的renameat2系统调用可以在同一个文件系统上原子地交换两条路径。然而,它甚至还没有一个 glibc 包装器,更不用说访问它的 coreutils 方式了。所以它看起来像这样:

int dirfd = open(".../base", O_PATH | O_DIRECTORY | O_CLOEXEC);
syscall(SYS_renameat2, dirfd, "alpha", dirfd, "bravo", RENAME_EXCHANGE);
close(dirfd);
system("rm -rf alpha");

(Of course, you should do proper error handling etc. – see this gistfor a more sophisticated renameat2wrapper.)

(当然,您应该进行适当的错误处理等 -有关更复杂的包装器,请参阅此要点renameat2。)

That said – the symlink solution mentioned by others is both easier and portable, so unless bravoalready exists and you mustatomically update it, go with the symlink instead.

也就是说 - 其他人提到的符号链接解决方案既简单又便携,因此除非bravo已经存在并且您必须自动更新它,否则请改用符号链接。



2020 update: a glibc wrapper for this system call is available since glibc 2.28, released 2018-08-01 (Debian Stretch, Fedora 29). It's still not accessible via coreutils, though.

2020 年更新:自 2018 年 8 月 1 日发布的 glibc 2.28(Debian Stretch,Fedora 29)以来,可以使用此系统调用的 glibc 包装器。尽管如此,它仍然无法通过 coreutils 访问。

int dirfd = open(".../base", O_PATH | O_DIRECTORY | O_CLOEXEC);
renameat2(dirfd, "alpha", dirfd, "bravo", RENAME_EXCHANGE);
close(dirfd);
system("rm -rf alpha");

回答by Oddthinking

Use a separate, guaranteed atomic, operation to act as a semaphore.

使用单独的、有保证的原子操作来充当信号量。

So, if the creating and removing a file operations are atomic:

因此,如果创建和删除文件操作是原子的:

1) create a file called "semaphore".

1)创建一个名为“信号量”的文件。

2) If and only if that is successful (no conflict with existing file), do the operation (either process alpha or move the directory, depending on the process)

2)当且仅当成功(与现有文件不冲突)时,执行操作(进程 alpha 或移动目录,取决于进程)

3) rm semaphore.

3) rm 信号量。

回答by paxdiablo

If you mean atomic across both operations, I don't believe so. The closest would be:

如果你的意思是两个操作的原子性,我不这么认为。最接近的是:

mv alpha delta
mv bravo alpha
rm -rf delta

but that would still have a small window where alpha didn't exist.

但这仍然会有一个不存在 alpha 的小窗口。

To minimize the likelihood of anything trying to use alpha while it's not there you could (if you have the authority):

为了最大限度地减少任何尝试使用 alpha 的可能性,而您可以(如果您有权限):

nice --20 ( mv alpha delta ; mv bravo alpha )
rm -rf delta

which will crank up your process priority substantially while the mvoperations are happening.

这将在mv操作进行时大幅提高您的流程优先级。

If, as you say in your addendum, there's only one place that checks alpha and it errors if it's not there, you could change that code to not error immediately, but try again in a short time (easily sub-second for two mvoperations) - these retries should alleviate any problem unless you're replacing alpha veryfrequently.

如果,正如你在附录中所说的,只有一个地方检查 alpha 并且如果它不存在就会出错,你可以将该代码更改为不立即出错,但在短时间内重试(两个mv操作很容易在亚秒级) - 这些重试应该可以缓解任何问题,除非您非常频繁地更换 alpha 。

回答by seh

The SQLitedocumentation section File Locking and Concurrency in SQLite Version 3has a well-written description of its escalating locking protocol to control concurrent reading, exclusive writing, and rollback after a crash. Some of those ideas apply here.

SQLite 版本 3 中SQLite文档部分File Locking and Concurrency对其用于控制​​并发读取、独占写入和崩溃后回滚的升级锁定协议进行了精心编写的描述。其中一些想法在这里适用。

回答by Peter

This should do the trick:

这应该可以解决问题:

mkdir bravo_dir alpha_dir
ln -s bravo_dir bravo
ln -s alpha_dir alpha
mv -fT bravo alpha

strace mv -fT bravo alpha shows:

strace mv -fT bravo alpha 显示:

rename("bravo", "alpha")

which looks pretty atomic to me.

这对我来说看起来很原子。

回答by Tyler McHenry

Even if you were accessing the inodes directly there would still be no way to atomically swap the inode values in user-space.

即使您直接访问 inode,仍然无法在用户空间中以原子方式交换 inode 值。

回答by Loren Pechtel

Worrying about the atomic nature of the operation is meaningless. The thing is, the access to alpha by the other task will not be atomic anyway.

担心操作的原子性是没有意义的。问题是,其他任务对 alpha 的访问无论如何都不是原子的。

Oddthinking's semaphore approach is the only way to go.

Oddthinking 的信号量方法是唯一的出路。

If you can't modify the other task then you'll have to ensure it's not running before doing the replacement.

如果您无法修改其他任务,则必须在进行替换之前确保它没有运行。