我正在编写一个脚本,用于使用 rsync 自动将文件从服务器 a 复制到服务器 b 这是我的脚本:
#!/bin/bash
NOW=$(date +"%Y-%m")
rsync -au --ignore-existing /var/www/uploads/$NOW/* -e [email protected]:/var/www/uploads/$NOW/.
当我们要去下个月,比如从十月到十一月时,我的脚本出现这个错误:
`
rsync: mkdir "/var/www/uploads/2014-11/." failed: No such file or directory (2)
rsync error: error in file IO (code 11) at main.c(605) [Receiver=3.0.9]
rsync: connection unexpectedly closed (9 bytes received so far) [sender]
rsync error: error in rsync protocol data stream (code 12) at io.c(605) [sender=3.0.9]
`我该如何修复这个错误?请帮帮我
答案1
首先,确保目标上的父目录存在。即[email protected]:/var/www/uploads
应该存在。我认为按照您的表述,尾随.
是指 rsync 尝试创建的目录,除非父目录已经存在,否则它无法这样做。即父目录是[email protected]:/var/www/uploads/$NOW
。
其次,要意识到 rsync 的行为与 cp 的行为在文件名末尾带有“/”方面有细微的不同。我发现最安全、最直观的方法是/
在所有目录末尾使用尾随符号。如下所示:
rsync -au --ignore-existing /var/www/uploads/$NOW/ -e [email protected]:/var/www/uploads/$NOW/
与 cp 不同,rsync 会可靠地将源参数中的目录内容复制到目标目录的内容中,如果需要则创建目标目录(尽管它的父目录必须存在),并且如果目标已经存在则不将源目录(即父目录)放入目标目录中。
这与您所做的稍有不同,因为您执行的方式会排除源目录中名称以 开头的文件.
,并且如果复制的文件列表太长将会失败(如果我没记错的话,bash 扩展的总命令行长度上限为大约 32K 个字符)。
答案2
需要删除结尾的点,将脚本更改为:
rsync -au --ignore-existing /var/www/uploads/$NOW/* -e [email protected]:/var/www/uploads/$NOW
答案3
尝试让 rsync 在目标上创建所有需要的目录:
NOW=$(date +"%Y-%m")
rsync -au --ignore-existing --relative /var/www/uploads/$NOW [email protected]:/var/www/uploads/
- 添加
--relative
以让 rsync 在目标上创建目录树。 - 源之后没有斜杠,因此最后一个目录(“
$NOW
”)也会在目标上创建,而不仅仅是它的内容。 - 从目标中删除“$NOW”,因为如果需要它将被创建。
- 删除了看起来像是错误的孤独
-e
选项(它需要一个参数:“此选项允许您选择用于通信的备用远程 shell 程序“)。
或者查看这个答案了解更多详细信息和替代方案。
答案4
我在本地运行 rsync(从 python 脚本)时遇到了此错误,不是通过 dir,而是通过 os.walk 循环遍历目录和子目录以找到包含文件的文件夹,然后使用带有 starmap 的 multiprocessing.pool rsync 每个文件。我搜索了很长时间才找到这种情况的答案,最后我找到了此链接提出解决方案;
通过 os.walk,我获取了根目录,如果有与该根目录关联的文件,则我使用 -aR 标志调用 rsync;
import subprocess
import os
import sys
import multiprocessing
from util_subprocess import exec_subprocess
ORIG_SRC_ROOT = '/home/username/workspaces/data/prod'
# IMPORTANT (slash, period) - we need to tell rsync
# that we want it to create subdirs relatively after the period
# in the DEST_ROOT dir
REL_SRC_ROOT = ORIG_SRC_ROOT + '/.'
# rsync will create the directory structure for you below the DEST_ROOT
DEST_ROOT = '/home/username/workspaces/data/prod_bak'
CPU_COUNT = multiprocessing.cpu_count()
if(not CPU_COUNT):
CPU_COUNT = 1
def sync(src, dest):
'''
Make the call the subprocess.run to execute rsync.
NOTE:
The flags are key here:
-- '-a' (-a, --archive archive mode; equals -rlptgoD (no -H,-A,-X))
is pretty standard and covers most everything we need.
-- '-R'(-R, --relative use relative path names),
makes sure rsync creates the subdirs on the destination folder.
'''
flags: str = r"-aR"
program_name: str = r"rsync"
exec_list = [program_name, flags , src, dest]
return exec_subprocess(exec_list)
def get_sync_params():
'''Get a list of tuples, each tuple containing the src_file path, and dest_dir path.'''
sync_params = []
for root, dirs, files in os.walk(ORIG_SRC_ROOT, topdown=False):
if(files):
for file in files:
src_file_temp = os.path.join(root, file)
# replace the orig root dir with the relative one from above
src_file = src_file_temp.replace(ORIG_SRC_ROOT, REL_SRC_ROOT)
dest_dir = DEST_ROOT
# place them into a tuple
sync_tuple = (src_file, dest_dir)
# add the tuple to the list
sync_params.append(sync_tuple)
return sync_params
if __name__ == "__main__":
'''Create a pool for multiprocessing and get these done in parallel.'''
# call the get_sync_params above
sync_params = get_sync_params()
# Create a pool of specific number of CPUs
print("Create {} processes in our pool.".format(CPU_COUNT))
pool = multiprocessing.Pool(processes=CPU_COUNT)
'''
Start each task within the pool -
call the 'sync' function above
(which calls a separate wrapper function
to the subprocess.run call (exec_subprocess)).
The return value is a list of tuples
and each tuple contains the output (stdout OR stderr)
and the return_code results for each pair of src & dest input.
'''
results = pool.starmap(sync, sync_params)
因此,我们本质上创建了一个元组列表,其中每个元组都是 src_file 路径和 dest_dir 路径,由 multiprocessing.pool.starmap(function_name, tuple_list) 处理,后者又使用池中进程处理每个元组,并通过 subprocess.run 将其发送到 rsycn。列表中的元组可能如下所示;
[('/home/username/workspaces/data/prod/./math/alg_1/alg_1_test.txt',
'/home/username/workspaces/data/prod_bak/'), ('...', '...'), ]
第一个字符串是 src_file 路径,第二个是 dest_dir 路径。请注意 src_file 路径中的 '.'(告诉 rsync 我们需要在目标中在此之后创建子目录),以及 dest_dir 路径中的尾随 '/',告诉 rsync 将这些新子目录放在此目标根目录下。