【问题标题】:rsync --delete --files-from=list / dest/ does not delete unwanted filesrsync --delete --files-from=list /dest/ 不会删除不需要的文件
【发布时间】:2010-12-21 07:08:54
【问题描述】:

正如您在标题中看到的那样,我尝试将文件夹与文件列表同步。我希望这个命令会删除 dest/ 中所有不在列表中的文件,但它没有。

所以我搜索了一下,现在知道了,rsync 不能做到这一点。

但我需要它,所以你知道有什么方法吗?

PS:该列表是由python脚本创建的,因此可以想象您的解决方案使用了一些python代码。

编辑,让我们具体一点:

列表如下所示:

/home/max/Musik/Coldplay/Parachutes/Trouble.mp3
/home/max/Musik/Coldplay/Parachutes/Yellow.mp3
/home/max/Musik/Coldplay/A Rush of Blood to the Head/Warning Sign.mp3
/home/max/Musik/Coldplay/A Rush of B-Sides to Your Head/Help Is Around the Corner.mp3
/home/max/Musik/Coldplay/B-Sides (disc 3)/Bigger Stronger.mp3

和这样的命令:

rsync --delete --files-from=/tmp/list / /home/max/Desktop/foobar/

这可行,但如果我删除一行,它不会在 foobar/ 中删除。

编辑 2:

rsync -r --include-from=/tmp/list --exclude=* --delete-excluded / /home/max/Desktop/foobar/

这两个都不起作用...

【问题讨论】:

  • 顺便说一句:rsync 版本 3.0.6 协议版本 30 忘记了,抱歉
  • 我最讨厌 rsync 的一件事是缺乏对您所要求的确切内容的支持。好帖子。

标签: rsync


【解决方案1】:

也许您可以改为使用包含模式列表来执行此操作,并使用--delete-excluded(顾名思义)?比如:

rsync -r --include-from=<patternlistfile> --exclude=* --delete-excluded / dest/

如果文件名可能包含通配符(*?[),那么您可能需要修改 Python 以将它们转义:

re.sub("([[*?])", r"\\\1", "abc[def*ghi?klm")

编辑: 基于模式的匹配与--files-from 的工作方式略有不同,因为出于效率原因,rsync 不会递归到与排除模式匹配的目录中。因此,如果您的文件位于 /some/dir/some/other/dir 中,那么您的模式文件需要如下所示:

/some/
/some/dir/
/some/dir/file1
/some/dir/file2
/some/other/
/some/other/dir/
/some/other/dir/file3
...

或者,如果所有文件都在同一个目录中,那么你可以稍微重写命令:

rsync -r --include-from=<patternlistfile> --exclude=* --delete-excluded /some/dir/ dest/

然后你的模式变成:

/file1
/file2

编辑:考虑一下,您可以使用一种模式包含所有目录:

/**/

但是你最终会得到dest/ 中的整个目录树,这可能不是你想要的。但是将它与-m (修剪空目录)结合起来应该可以解决这个问题 - 所以命令最终会是这样的:

rsync -m -r --delete-excluded --include-from=<patternfile> --exclude=* / dest/

和模式文件:

/**/
/some/dir/file1
/some/other/dir/file3

【讨论】:

  • 也谢谢你,但你的命令要求 -d 或 -r 都不起作用。
  • 文件是否在子目录中?如果是这样,目录(及其父目录)也需要在模式列表中,否则 rsync 甚至不会递归到它们中。
  • 我认为在这一点上,首先在你的系统上测试你的命令是值得的:P
  • 在这里工作正常(除了第一次错过 -r ——这会教我重新输入)。不过,我希望您检查手册页并根据您的情况调整我的命令,而不是盲目地复制+粘贴;)
  • 实际上,您可能不需要这样做 - 取决于您要对空目录执行的操作。即将进行另一个编辑...
【解决方案2】:

这并不完全是解决方案,但来到这里的人可能会发现这很有用:由于 rsync 3.1.0 有一个 --delete-missing-args 参数,当您使用 --files-from 同步两个目录时,它会删除目标目录中的文件。您需要在 /tmp/list 中指定已删除的文件以及要复制的文件:

rsync --delete-missing-args --files-from=/tmp/list /source/dir /destination/dir

更多详情请见the man page

【讨论】:

  • 看起来很有希望,但我误解了这个选项的作用。如果它们在 --files-from 列表中列出但在源上找不到,它只会删除目标上的文件。
  • 这不正是我们所需要的。
【解决方案3】:

正如你所解释的,命令

rsync -r --delete --files-from=$FILELIST user@server:/ $DEST/

当 $FILELIST 中的条目已被删除时,不会删除目标中的内容。一个简单的解决方案是使用以下内容。

mkdir -p $DEST
rm -rf $TEMP
rsync -r --link-dest=$DEST --files-from=$FILELIST user@server:/ $TEMP/
rm -r $DEST
mv $TEMP $DEST

这指示 rsync 使用空目标。 link-dest-directory 中已经存在的文件是本地硬链接的,而不是复制的。最后,旧目的地被新目的地取代。如果 $DEST 不存在,第一个 mkdir 创建一个空的 $DEST,以防止 rsync 错误。 (假设 $-变量携带相应文件或目录的完整路径。)

硬链接有一些小的开销,但您不需要弄乱复杂的包含/排除策略。

【讨论】:

  • 有趣的解决方案!太糟糕了,同步本地服务器时没有这样的技巧。
【解决方案4】:

灵感来自 m4t,但使用 ... rsync 进行清理

rsync -r --link-dest=$dst --files-from=filelist.txt user@server:$source/ $temp
rsync -ra --delete --link-dest=$temp $temp/ $dest

【讨论】:

  • 'rm -rf $TEMP' 在你的代码之前需要,否则,如果 $temp 中有不需要的文件,它们最终会在 $dest 中。
  • 我认为这里有一个错字,不确定在哪里
【解决方案5】:

Explicit build --exclude-from=... 似乎是同步文件列表的唯一方法。

stdin = subprocess.PIPE
other_params.append("--exclude-from=-") #from stdin 

p = subprocess.Popen( 'rsync -e ssh -zthvcr --compress-level=9 --delete'.split() + other_params + [src, dst], stdin =  PIPE)

if relative_files_list != None:
    #hack: listing of excluded files seems the only way to delete unwanted files at destination
    files = set(map(norm_fn, relative_files_list)) #make hash table, for huge lists
    for path, ds, fs in os.walk(src):
        for f in fs:
            rel_path_f = norm_fn(os.path.relpath(os.path.join(path, f), src))
            if rel_path_f not in files:
                #print 'excluding', rel_path_f.replace('\\', '/')
                p.stdin.write(rel_path_f + '\n')
    p.stdin.close()
assert 0 == p.wait()

【讨论】:

    【解决方案6】:

    我知道很久以前有人问过这个问题,但我对答案并不满意。

    假设播放列表由mpd 创建,以下是我解决问题的方法:

    #!/bin/bash                                                                 
    
    playlist_path="/home/cpbills/.config/mpd/playlists"
    playlist="${playlist_path}/${1}.m3u"
    music_src="/home/cpbills/files/music"
    music_dst="/mnt/sdcard/music/"
    
    if [[ -e "$playlist" ]]; then
      # Remove old files
      find "$music_dst" -type f | while read file; do
        name="$(echo "$file" | sed -e "s!^$music_dst!!")"
        if ! grep -qF "$name" "$playlist"; then
          rm "$file"
        fi
      done
    
      # Remove empty directories
      find "$music_dst" -type d -exec rmdir {} \; 2>/dev/null
    
      rsync -vu \
          --inplace \
          --files-from="$playlist" \
          "$music_src" "$music_dst"
    else
      printf "%s does not exist\n" "$playlist" 1>&2
      exit 1
    fi
    

    【讨论】:

      【解决方案7】:

      rsync 非常适合保持目录同步,以及其他有用的功能。如果您在 SOURCE 上确实有准确的副本,并且想要删除 DEST 上的文件,您可以从 SOURCE 中删除它们,rsync --delete 选项也会从 DEST 中删除它们。

      但是,如果您只是想删除任意文件列表,我建议您使用 SSH 来完成:

      ssh user@remote.host.com rm /path/to/file1 /path/to/file2
      

      这将在远程主机上执行rm 命令。

      使用 python,您可以:

      import subprocess
      FileList = ['/path/to/file1', '/path/to/file2']
      subprocess.call(['ssh', 'dAnjou@my.server.com', 'rm'] + FileList)
      

      ~好好享受

      【讨论】:

      • 误会。我没有要删除的文件列表。我有一个要复制的文件列表。我希望删除那些不在列表中的文件。不过谢谢你的回答。
      猜你喜欢
      • 2012-05-05
      • 2017-05-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-02-24
      • 2019-01-27
      • 2018-11-05
      • 2021-03-13
      相关资源
      最近更新 更多