使用Python中的subprocess.call调用rsync

16

我正在尝试在Python脚本的子进程中通过ssh执行rsync,将一台服务器上的图片复制到另一台服务器。我定义了一个函数:

def rsyncBookContent(bookIds, serverEnv):
    bookPaths = ""
    if len(bookIds) > 1:
        bookPaths = "{" + ",".join(("book_"+str(x)) for x in bookIds) + "}"
    else:
        bookPaths = "book_" + str(bookIds[0])

    for host in serverEnv['content.hosts']:
        args = ["rsync", "-avz", "--include='*/'", "--include='*.jpg'", "--exclude='*'", "-e", "ssh", options.bookDestDir + "/" + bookPaths, "jill@" + host + ":/home/jill/web/public/static/"]
        print "executing " + ' '.join(args)
        subprocess.call(args)

我最终想做的是让Python执行这个命令(在bash shell下有效):

rsync -avz --include='*/' --include='*.jpg' --exclude='*' -e ssh /shared/books/{book_482,book_347} jill@10.12.27.20:/home/jill/web/public/static/

实际上,我的打印语句输出:

executing rsync -avz --include='*/' --include='*.jpg' --exclude='*' -e ssh /shared/books/{book_482,book_347} jill@10.12.27.20:/home/jill/web/public/static/

但是,如果从这个 Python 脚本中执行,会遇到两个问题:

  1. 如果 bookIds 的长度大于 1,则 /shared/books/ 目录下的子目录列表会被 bash 或 rsync 错误地解释。错误信息为:
    • rsync: link_stat "/shared/books/{book_482,book_347}" failed: No such file or directory (2))
  2. 如果 bookIds 的长度等于 1,则源目录下的 所有文件 都会被同步(而不仅仅是 *.jpg 文件,我本意只同步这些文件)。

看起来像是 subprocess.call 函数需要对某些字符进行转义或其他操作,是吗?


纯属好奇,如果在调用中设置 shell=True 会发生什么?例如:subprocess.call(args, shell=True) - sberry
1个回答

22

我解决了自己的问题。我的问题源于我误解了subprocess.call函数执行和bash中花括号内列表的扩展方式。

当我在bash shell中使用带有花括号的子目录发出rsync命令时,bash实际上将其扩展为多个参数,并将这些参数传递给rsync(/shared/books/book_1、shared/books/book_2等)。但是,如果将具有花括号的相同字符串“/shared/books/{book_1, book_2}”传递给subprocess.call函数,则扩展不会发生,因为它没有通过bash,所以我传递给rsync的参数实际上是“/shared/books/{book_1, book_2}”。

同样地,对于文件模式(‘*’,‘*.jpg’等),单引号在bash命令行上有效(只有单引号内的值被传递给rsync),但在subprocess.call内部,单引号作为文件模式传递给rsync(“‘*.jpg’”)。

新的(工作)代码看起来像这样:

def rsyncBookContent(bookIds, serverEnv):
    bookPaths = []
    for b in bookIds:
        bookPaths.append(options.bookDestDir + "/book_" + str(b))
    args = []
    for host in serverEnv['content.hosts']:
        # copy all *.jpg files via ssh
        args = ["rsync", "-avz", "--include", "*/", "--include", "*.jpg", "--exclude", "*", "-e", "ssh"]
        args.extend(bookPaths)
        args.append("jill@" + host + ":/home/jill/web/public/static/"])
        print "executing " + ' '.join(args)
        subprocess.call(args)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接