Вызов rsync из python subprocess.call
Я пытаюсь выполнить rsync через ssh из подпроцесса в python script, чтобы скопировать изображения с одного сервера на другой. У меня есть функция, определенная как:
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, "[email protected]" + host + ":/home/jill/web/public/static/"]
print "executing " + ' '.join(args)
subprocess.call(args)
В конечном итоге я пытаюсь выполнить Python (что работает из оболочки bash):
rsync -avz --include='*/' --include='*.jpg' --exclude='*' -e ssh /shared/books/{book_482,book_347} [email protected]:/home/jill/web/public/static/
И действительно, моя заявка на печать выводит:
executing rsync -avz --include='*/' --include='*.jpg' --exclude='*' -e ssh /shared/books/{book_482,book_347} [email protected]:/home/jill/web/public/static/
Но при выполнении из этого python script возникают две проблемы:
- Если len (bookIds) > 1, список подкаталогов под/shared/books/каким-то образом неверно истолкован bash или rsync. Сообщение об ошибке:
- rsync: link_stat "/shared/books/{book_482, book_347}" не удалось: нет такого файла или каталога (2))
- Если len (bookIds) == 1, все файлы под исходным каталогом rsynced (а не только *.jpg, как и мои намерения)
Кажется, что функция subprocess.call требует, чтобы некоторые символы были экранированы или что-то еще, no?
Ответы
Ответ 1
Выяснил мои проблемы. Мои проблемы были результатом моего непонимания того, как выполняется функция subprocess.call, и bash расширение списков внутри фигурных скобок.
Когда я выдавал команду rsync в оболочке bash с подкаталогами в фигурных скобках, 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("[email protected]" + host + ":/home/jill/web/public/static/"])
print "executing " + ' '.join(args)
subprocess.call(args)