Странная ошибка Python с subprocess.check_call

У меня действительно странная ошибка с функцией python subprocess.check_call(). Вот два теста, которые должны завершиться ошибкой из-за проблем с правами доступа, но первый возвращает только «использование» («неожиданное поведение»):

# Test #1
import subprocess
subprocess.check_call(['git', 'clone', 'https://github.com/achedeuzot/project',
                       '/var/vhosts/project'], shell=True)

# Shell output
usage: git [--version] [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]
           [-p|--paginate|--no-pager] [--no-replace-objects] [--bare]
           [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]
           [-c name=value] [--help]
           <command> [<args>]

The most commonly used git commands are:
[...]

Теперь для второго теста («ожидаемое поведение»):

# Test #2
import subprocess
subprocess.check_call(' '.join(['git', 'clone', 'https://github.com/achedeuzot/project',
                                '/var/vhosts/project']), shell=True)
# Here, we're making it into a string, but the call should be *exactly* the same.

# Shell output
fatal: could not create work tree dir '/var/vhosts/project'.: Permission denied

Эта вторая ошибка является правильной. У меня действительно нет разрешений. Но почему между этими двумя вызовами есть разница? Я думал, что использование одной строки или списка аналогично функции check_call(). Я прочитал документацию по python и различные примеры использования, и оба выглядят правильно.

У кого-нибудь была такая же странная ошибка? Или кто-нибудь знает, почему есть разница в выводе, когда команды должны быть одинаковыми?

Дополнительные примечания: Python 3.4


person achedeuzot    schedule 10.09.2014    source источник


Ответы (2)


Удалите shell=True из первого. Если вы внимательно перечитаете документацию модуля подпроцесса, вы увидите. Если shell=False (по умолчанию), то первым аргументом является список командной строки с аргументами и всеми (или строка только с командой, без аргументов вообще). Если shell=True, то первый аргумент - это строка, представляющая командную строку оболочки, выполняется оболочка, которая, в свою очередь, анализирует командную строку для вас и разделяет пробелами (+ гораздо более опасные вещи, которые вы, возможно, не хотите, чтобы она делала ). Если shell=True и первый аргумент является списком, то первый элемент списка является командной строкой оболочки, а остальные передаются в качестве аргументов оболочке, а не команде.

Если вы не знаете, что вам действительно нужно, всегда позволяйте shell=False.

person Anton    schedule 10.09.2014
comment
Благодарю вас ! Я пропустил это! Я использовал shell=True, потому что другая команда не работала без этой опции. Поэтому я просто сохранил его для последующих вызовов check_call. - person achedeuzot; 11.09.2014

Вот соответствующий бит из документации:

Если args представляет собой последовательность, первый элемент определяет командную строку, а любые дополнительные элементы будут рассматриваться как дополнительные аргументы для самой оболочки. То есть Popen делает эквивалент:

Popen(['/bin/sh', '-c', args[0], args[1], ...])

person Jesse W at Z - Given up on SE    schedule 10.09.2014
comment
Я знаю это, я хочу, чтобы они были command + args... Я не понимаю, как это что-то объясняет в странном поведении... - person achedeuzot; 11.09.2014
comment
Странное поведение связано с тем, что слово клон рассматривается как аргумент /bin/sh, а не как аргумент git. - person Jesse W at Z - Given up on SE; 11.09.2014
comment
Да, но это происходит из-за того, что shell=True находится в check_call аргументах. Без этого все они являются аргументами команды git :) - person achedeuzot; 11.09.2014
comment
Да, и есть ли разница в поведении без shell=True? - person Jesse W at Z - Given up on SE; 11.09.2014
comment
Конечно ! Но это было то, что я пропустил, когда смотрел примеры. - person achedeuzot; 11.09.2014
comment
Э, я думаю, что мы говорим мимо друг друга. В любом случае, похоже, что у вас это работает сейчас. - person Jesse W at Z - Given up on SE; 11.09.2014
comment
Ну похоже так. Это работает, потому что я удалил shell=True, поэтому список рассматривается как командная строка оболочки без каких-либо дополнительных аргументов для оболочки. Ответ @Anton объясняет это очень хорошо :) - person achedeuzot; 11.09.2014
comment
Я согласен с ответом Антона - я только что писал свой ответ в то же время и подумал, что было бы полезно процитировать документы. - person Jesse W at Z - Given up on SE; 11.09.2014