我正在尝试运行 Home 中所有 .x 文件夹的备份以保存配置。
使用我正在编写的程序,您可以选择要包含在备份中或从备份中排除的任何文件夹,此列表是动态的。
我正在尝试构建类似的东西:
rsync -aP --exclude $Home/.cache/google-chrome/Default/Cache/* --exclude $Home/.cache/google-chrome/Default/Media\ Cache/* --exclude $Home/.wine $HOME/.* /mnt/ext/
因此,在上面的例子中,除了 Wine 和 Chrome 的缓存之外,所有内容都复制到了外部驱动器。我知道我可以建立一个文本文件并告诉,rsync --exclude-from 'textfile'
但我宁愿不必读取和重写文本文件。
我担心的是,随着它的扩展,排除列表可能会变得很长,因为我的项目最终将开始包括 $HOME 之外的文件夹以及其他系统的 NFS 共享。我可以启动命令并在变量中使用排除列表:
exlist = "--exclude $Home/.cache/google-chrome/Default/Cache/* --exclude $Home/.cache/google-chrome/Default/Media\ Cache/* --exclude $Home/.wine" etc etc
subprocess.Popen("rsync -options ",exlist," /source /dest")
但是,这是否会导致非常长的 shell 命令出现问题,以至于我必须将其分解为较小的块?我宁愿提前预防潜在的未来问题,并开始编写处理程序来完成此任务,而不是尝试稍后修复和修补它。
答案1
我发现我的大小限制不一定是命令的长度,而是参数的长度。 Shell 命令的长度可以是 100,000-200,000 个字符。 但是,参数的长度限制为几百个字符,具体取决于系统,这是通过使用 发现的getconf ARG_MAX
。
当我在参数中添加许多文件夹排除项时,这最终导致空间不足的问题rsync
。
为了解决这个问题,我需要以编程方式将rsync
一次长时间执行的过程拆分为多个较小的过程。
执行此操作需要“边做边构建”,直到达到极限。程序将尝试构建所有单个排除项的字符串,每个排除项都位于一个列表(数组)中。如果超出参数长度限制,它将重新解析当前列表以进行更广泛的排除,从而减少总数,并存储这些排除的排除项。
以下是正在构建的排除列表的示例:
该列表称为excludes
(long list items...)
--exclude $Home/.wine
--exclude $Home/.cache/somefolders/*
--exclude $Home/.cache/somefolders/*
--exclude $Home/.cache/somefolders/*
--exclude $Home/.cache/somefolders/*
--exclude $Home/.cache/google-chrome/Default/Cache/*
--exclude $Home/.cache/google-chrome/Default/Media\ Cache/*
**LIMIT EXCEEDED**
现在,我们的 ARGS 限制已经超出。程序将重新解析此列表以查找常用文件夹,并创建第二个列表以保存以供下次执行rsync
。
解析后excludes
重建为如下列表:
(long list items...)
--exclude $Home/.wine
--exclude $Home/.cache/*
在解析时,被删除的行被放入另一个列表中,我们称之为它excludesnext
看起来像这样:
--exclude $Home/.cache/somefolders/*
--exclude $Home/.cache/somefolders/*
--exclude $Home/.cache/somefolders/*
--exclude $Home/.cache/somefolders/*
--exclude $Home/.cache/google-chrome/Default/Cache/*
--exclude $Home/.cache/google-chrome/Default/Media\ Cache/*
因此,所有重复文件夹都从 中移除excludes
并放入 中excludesnext
。随着项目被移动,缩短的宽文件夹被替换为,将使用这些新的较短文件夹创建excludes
一个名为 的新列表。cutlist
因此,该cutlist
列表包括:
--exclude $Home/.cache/*
rsync
excludes
使用列表作为参数运行。
rsync
完成后,excludes
列表将被清空,并将excludesnext
列表复制到excludes
列表中。
然后使用新的缩短列表作为源rsync
循环运行,并将所有匹配的子文件夹用作排除。cutlist
excludes
当然,cutlist
列表中可能有许多项目,程序将循环遍历每个项目。第二次运行rsync
循环运行。但在此循环中,当达到最大长度时,excludes
列表的解析方式与之前相同,只是新的缩短文件夹被添加到cutlist
而不是进入excludes
。这样,当循环遍历 cutlist 时,它可以在遍历列表时自行扩展。当excludesnext
使用列表项时,它们将被删除。
最终,这将导致所有文件夹和子文件夹被复制,包括所有排除项,无论有多少。完成最后一项意味着cutlist
一切都已执行。
此方法可能会rsync
运行几次甚至几十次,具体取决于您排除了多少个文件夹,但它永远不会重复。
抱歉,我没有发布实际的代码,但这是一个正在进行的混乱的工作,在我发布可运行的服务之前,我还不太愿意发布我的代码。
答案2
我知道我可以建立一个文本文件并告诉 rsync --exclude-from 'textfile' 但我不想读取和重写文本文件。
做就是了:
with contextlib.closing(tempfile.NamedTemporaryFile()) as exclude_from:
print(*your_exclude_list, sep="\n", flush=True, file=exclude_from) # etc
subprocess.check_call(['rsync', '--exclude-from', exclude_from.name, ...])
...不用担心临时文件。我知道处理临时文件似乎很麻烦,但使用 Python 的库和上下文管理器,这些都可以很好地包装起来,所以你不必担心它。