代码之家  ›  专栏  ›  技术社区  ›  dirtside

以原子方式移动目录

  •  31
  • dirtside  · 技术社区  · 16 年前

    我在同一个父目录中有两个目录。调用父目录 基础 还有孩子们 好极了 . 我想换一个 具有 好极了 . 最简单的方法是:

    rm -rf alpha
    mv bravo alpha
    

    阿尔法 具有 ? 如果不是,有没有复杂的方法?

    顺便说一句,如果目录在短时间内不存在,这不是一个无法克服的问题。只有一个地方尝试访问alpha,它会在执行任何关键操作之前检查alpha是否存在。如果没有,它将给出一条错误消息。但如果有办法做到这一点,那就太好了。:)也许有办法直接修改索引节点,或者。。。

    15 回复  |  直到 16 年前
        1
  •  56
  •   Community kavare    12 年前

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

    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
    

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

        2
  •  21
  •   Daniel S. Sterling    13 年前

    如果使用符号链接,则可以执行此操作:

    假设alpha是指向目录alpha_1的符号链接,您希望将符号链接切换到指向alpha_2。以下是切换前的情况:

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

    要使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
    

    现在,您可以删除旧目录:

    $ rm -rf alpha_1
    

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

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

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

    $ ln -nsf alpha_3 alpha
    $ rm -rf alpha_2
    
        3
  •  15
  •   mssaxm    11 年前

    -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 ./
    

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

        4
  •  11
  •   Lucas Werkmeister overseas    5 年前

    自Linux3.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");
    

    (当然,您应该进行适当的错误处理等。请参阅 this gist 对于一个更复杂的 重命名2 包装纸。)

    也就是说,其他人提到的符号链接解决方案既简单又可移植,因此除非 bravo 必须 用符号方式更新链接。


    2020年更新:自2018-08-01发布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");
    
        5
  •  8
  •   Oddthinking    16 年前

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

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

    2) 如果且仅当该操作成功(与现有文件无冲突),则执行该操作(处理alpha或移动目录,具体取决于进程)

    3) rm信号量。

        6
  •  6
  •   paxdiablo    16 年前

    mv alpha delta
    mv bravo alpha
    rm -rf delta
    

    但在阿尔法不存在的地方,仍然会有一个小窗口。

    为了最大限度地降低在alpha不存在时尝试使用alpha的可能性,您可以(如果您有权限):

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

    mv 行动正在进行。

    mv 操作)-这些重试应该可以缓解任何问题,除非您更换alpha 非常 频繁地

        7
  •  4
  •   seh Alexei    15 年前

    SQLite 文件科 File Locking and Concurrency in SQLite Version 3 对其升级锁定协议进行了详细描述,以控制并发读取、独占写入和崩溃后回滚。其中一些想法适用于这里。

        8
  •  4
  •   Peter    11 年前

    这应该可以做到:

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

    rename("bravo", "alpha")
    

    这对我来说是非常原子的。

        9
  •  1
  •   Tyler McHenry    16 年前

    即使您直接访问inode,也无法在用户空间中自动交换inode值。

        10
  •  1
  •   Loren Pechtel    16 年前

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

    如果您不能修改其他任务,那么在进行替换之前,您必须确保它没有运行。

        11
  •  0
  •   Chris Charabaruk    16 年前

    我不相信有任何原子方法可以做到这一点。您的最佳选择是执行以下操作:

    mv alpha delme
    mv bravo alpha
    rm -rf delme
    
        12
  •  0
  •   Brian C. Lane    16 年前

        13
  •  0
  •   Philip Reynolds    15 年前

    mv和ln可用于原子操作。我使用ln(1)以原子方式部署web应用程序。

    ln -nsf <target> <link_name>
    

    例如

    $ mkdir dir1
    $ mkdir dir2
    $ ln -s dir1 mylink
    $ ls -l mylink
    lrwxrwxrwx  1 phil phil 4 Nov 16 14:45 mylink -> dir1
    $ ln -nsf dir2 mylink
    $ ls -l mylink
    lrwxrwxrwx  1 phil phil 4 Nov 16 14:46 mylink -> dir2
    
        14
  •  0
  •   Tomilov Anatoliy    7 年前

    Z 这里)使用 unionfs-fuse :

    # mkdir a b c Z
    # touch a/1 b/2 c/3
    # ln -s a X
    # ln -s b Y
    # unionfs X=RW:Y=RW Z
    # shopt -s globstar
    # file **
    a:   directory
    a/1: empty
    b:   directory
    b/2: empty
    c:   directory
    c/3: empty
    X:   symbolic link to a
    Y:   symbolic link to b
    Z:   directory
    Z/1: empty
    Z/2: empty
    # ln -sfn c Y
    # file **/*
    a:   directory
    a/1: empty
    b:   directory
    b/2: empty
    c:   directory
    c/3: empty
    X:   symbolic link to a
    X/1: empty
    Y:   symbolic link to c
    Y/3: empty
    Z:   directory
    Z/1: empty
    Z/3: empty
    # fusermount -u Z
    # rm -r a b c X Y Z
    
        15
  •  0
  •   William Hay    5 年前

    mount --bind bravo alpha

    它将alpha的内容隐藏起来,但是如果您想清除它,可以将父文件系统绑定到其他位置。

    如果文件系统是NFS导出的,您可能需要确保导出选项允许跨越文件系统边界,并在服务器上进行绑定装载。

    对于在alpha目录或子目录(例如cwd)上具有打开句柄的进程,您还需要小心。

        16
  •  -2
  •   Dan Fego    16 年前

    你为什么不这样做呢:

    rm -rf alpha/*
    mv bravo/* alpha/
    rm -rf bravo/
    

    那就意味着一切 在里面 阿尔法被摧毁了,阿尔法 从不 删除,并移动所有内容。