Проблемы с командой, использующей подстановочный знак * в подпроцессе

Я пытаюсь скопировать файлы из одного места в другое, используя библиотеку подпроцессов и метод Popen. При запуске следующего скрипта я получаю сообщение об ошибке cp: cannot stat /some/dev_path/*. Мне сказали, что * не расширяется до имен файлов, и вот в чем проблема. Также в некоторых других сообщениях люди предлагали использовать вызов вместо Popen, но, насколько я знаю, вызов не вернет stderr.

devPath = '/some/dev_path/'
productionPath = '/some/prod_path/'

p = subprocess.Popen(['cp', '-r', devPath + '*', productionPath], stdout = subprocess.PIPE, stderr = subprocess.PIPE)
pout, perr = p.communicate()

if perr != '':
    sys.exit('Error: ' + perr)

person marcin_koss    schedule 04.09.2012    source источник
comment
Расширение '*' является функцией используемой вами оболочки, за исключением того, что здесь вы не используете оболочку. См. docs.python.org/library/subprocess.html, чтобы узнать о нескольких способах добавления функциональность оболочки.   -  person Peter Rowell    schedule 04.09.2012
comment
Почему бы вместо этого не использовать shutil.copytree(), чтобы у вас было что-то переносимое, не требующее подпроцессов?   -  person Wooble    schedule 04.09.2012
comment
В Python для этого есть встроенная поддержка с использованием модулей Shutil и Glob. Вам действительно не нужно вызывать подпроцесс.   -  person Keith    schedule 04.09.2012


Ответы (1)


Расширение * (подстановка) — это функция вашей оболочки, например, bash. Поэтому вам придется использовать аргумент ключевого слова shell=True в вызове subprocess.Popen.

Однако в этом случае я настоятельно рекомендую вместо этого использовать shutil.copytree.

(Во-первых, потому что это намного проще (см. Zen of Python) и менее подвержены ошибкам.Работа с ошибками намного чище, вы получаете приятные исключения, включая список ошибок (для многофайловых операций, таких как ваша), и вам не нужно иметь дело с порождением подпроцесса и общением с ним. Во-вторых, разветвление дочернего процесса, если оно вам не нужно, является ненужной тратой ресурсов. Другие проблемы включают цитирование/экранирование и, возможно, введение уязвимостей в ваш код, если вы не сможете должным образом очистить пользовательский ввод.)

Например:

from shutil import copytree
from shutil import Error

try:
   copytree('dir_a', 'dir_b')
except (Error, OSError), e:
    print "Attempt to copy failed: %s" % e

Кроме того, вы не должны создавать пути файловой системы путем объединения строк, а вместо этого использовать os.path.join(). Это будет использовать правильный разделитель каталогов (os.sep) для текущей ОС и позволит вам легко писать переносимый код.

Пример:

>>> import os
>>> os.path.join('/usr/lib', 'python2.7')
'/usr/lib/python2.7'

Примечание. os.path.join по-прежнему выполняет только (умные) манипуляции со строками - ему все равно, доступен ли этот путь или даже существует.

person Lukas Graf    schedule 04.09.2012
comment
Спасибо за ваш ответ. Я не знал о шутиле. Почему шутил предпочтительнее подпроцесса? - person marcin_koss; 04.09.2012
comment
@marcin_koss Обновлен ответ, некоторые подробности о том, почему следует избегать ненужных подпроцессов. - person Lukas Graf; 05.09.2012