【问题标题】:Guaranteed atomic move of folder保证文件夹的原子移动
【发布时间】:2012-03-23 13:12:48
【问题描述】:

我有一个脚本在 XX:00 每分钟运行一次。该脚本循环遍历给定目录中的所有子文件夹,并对其中的文件执行操作;

folder=/path/to/directory #Starting directory
someerror=0 #Did we have an error?

#CD to directory. Does it even exist?
cd $folder
RETVAL=$?
[ $RETVAL -eq 0 ] && echo Success changing directory to $folder && mainfolderexist=1
[ $RETVAL -ne 0 ] && echo Failure changing directory to $folder && mainfolderexist=0

if [ $mainfolderexist -eq 1 ]; then
    shopt -s nullglob
    for dir in $folder/*/
    do
    thedirname=`basename $dir` #Get directory name
    #cd to sub dir. Does it even exist?
    cd $dir
    RETVAL=$?
    [ $RETVAL -eq 0 ] && echo Success changing directory to $dir && subfolderexist=1
    [ $RETVAL -ne 0 ] && echo Failure changing directory to $dir && subfolderexist=0
    if [ $subfolderexist -eq 1 ]; then
        #perform some operation on all files in this directory
        someApp -someArgs --name=$thedirname *
    else #sub folder doesn't exist
        someerror=1
        break
    fi

    #next folder
    done
else #main folder doesn't exist
    someerror=1
fi

#REPEAT (only if no errors occured)
if [ $someerror -eq 0 ]; then
at now + 1 minutes << END
/bin/bash "$0" "$@"
END
fi

我使用它的方式是,我使用 SFTP 将目录上传到服务器,到像 /home/incoming 这样的文件夹,一旦目录完全上传,我会将其移动到 /path/to/directory。现在这是我担心的部分。

到目前为止,我一直确保只在 XX:XX:02 和 XX:XX:50 之间移动目录,但这是否必要?我想在不考虑系统时间的情况下自动化上传+移动过程,所以我想知道;

  1. 如果某个目录在 XX:00 正在被移动 (mv "somedir" "/path/to/directory/somedir") 并且脚本运行并遍历所有目录,该怎么办?
  2. 如果系统在执行 mv 命令期间断电怎么办?如果目录最终会移动一半或类似的东西,我将不得不在执行上述脚本之前编写一个脚本来验证这一点。

【问题讨论】:

  • 这与您的问题有点正交,但是您如何知道目录何时“完全上传”?
  • 你知道你的源和目标是否在同一个文件系统上吗?如果没有,您可能需要为您的 cron 作业添加一些智能,以使其不接触仅部分复制的目录。
  • @larsks 我没有,这就是我现在手动进行操作的原因。我打算编写一个程序,让服务器知道对目录的期望,开始上传,并在期望与实际交易匹配时移动所述目录。或者类似的东西……除非你有更好的选择?
  • 如果您上传档案(使用 tar 或 zip 等)而不是目录,您可以使用 inotify 之类的东西在上传完成时触发事件……但这仅适用于单个文件.使用目录,你可以用一个标志文件来完成同样的事情(即在目录上传完成后,创建一个&lt;directory&gt;.finished 文件或其他东西)。即使您不选择 inotify 之类的东西,这实际上可能是一个好主意(您的脚本可以只查找 .finished 文件)。
  • @larsks:仅使用 inotify 与存档有缺点,如果连接中断,它将被 inotify 报告为关闭,因此您仍然需要检查存档是否确实完整。使用 zip 很容易,而使用 tar 或 gz 等其他格式则不然。最后通过上传过程重命名上传的数据是最安全的选择。

标签: linux bash


【解决方案1】:

如果你的源路径和目标路径在同一个文件系统上,那么mv 是一个原子操作。由于它实际上不涉及复制或以其他方式重新定位文件,因此您的目录永远不会处于“半移动”状态。

另一方面,如果您的源路径和目标路径位于不同文件系统上,那么 mv 实际上是一个副本,然后是整个树的删除,这可能需要大量的时间时间,如果被打断,会使事情处于半完成状态。

【讨论】:

  • 不幸的是,如果源和目标不在同一个文件系统上,则无法告诉 mv 失败,因此您必须确保设置正确。
  • 假设Linux,您可以在调用mv之前使用mountpoint命令来确定这两个路径是否在同一个文件系统上,但我怀疑这种情况没有意义——大概目标路径是固定的,所以不需要每次都检查。
  • 如果我在执行 mv 命令之前在源目录上运行 ls -s &gt;&gt; /home/folder_expectation/$thedirname_source 怎么样。然后在我的主脚本中的每个循环,我运行ls -s &gt;&gt; /home/folder_expectation/$thedirname_target,然后运行cmp /home/folder_expectation/$thedirname_source /home/folder_expectation/$thedirname_target。之后,如果不匹配,它将中断循环(或跳过当前项目)。这可以跨文件系统工作吗?
  • 如今OSX mv 根本不调用重命名可能会很有趣!所以我认为 bash 脚本在 OSX 上的行为可能不同,但看起来是一样的。
  • 在处理 NFS 时经常会遇到这种情况。一种廉价的解决方法是将文件/目录复制到同一文件系统上的临时位置,然后执行mv,这将是原子的。例如TMP="temp.$RANDOM" &amp;&amp; cp -a mydir /dest/dir/$TMP &amp;&amp; mv /dest/dir/$TMP /dest/dir/mydir
【解决方案2】:

我相信mv 是一个原子操作(移动目录只是重命名目录,它不会移动磁盘上的任何文件)。 mv 使系统调用rename 根据this 是原子的(受某些约束)。

如果 newpath 已经存在,它将被自动替换(取决于一些条件 - 请参阅下面的错误),因此尝试访问 newpath 的另一个进程不会发现它丢失。

【讨论】:

  • 问题是当它不使用重命名时。如果你还在,你能在帖子的某个地方提到这一点吗?当然,标记的答案已经说明了这一点,但只是为了确保。
【解决方案3】:

请记住,即使您在同一个磁盘和分区中,当其中一个(或两个)使用加密作为软件时,移动也不是原子的。例如:同一台机器上的 2 个用户在 /home 中有目录。如果其中一个使用加密的主目录,则移动将不是原子的,因为它需要在移动之前加密/解密或解密/加密或加密或解密文件

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-10-16
    • 1970-01-01
    • 2021-12-02
    • 1970-01-01
    • 1970-01-01
    • 2021-08-02
    相关资源
    最近更新 更多