В чем разница между subprocess.Popen ("echo $ HOME" ... и subprocess.Popen (["echo", "$ HOME"] - PullRequest
2 голосов
/ 08 января 2020

Я не могу понять, что это bash связанный или python подпроцесс, но результаты разные:

>>> subprocess.Popen("echo $HOME", shell=True, stdout=subprocess.PIPE).communicate()
(b'/Users/mac\n', None)
>>> subprocess.Popen(["echo", "$HOME"], shell=True, stdout=subprocess.PIPE).communicate()
(b'\n', None)

Почему во второй раз это просто перевод строки? Где отваливаются аргументы?

Ответы [ 3 ]

2 голосов
/ 08 января 2020

В документации по https://docs.python.org/2/library/subprocess.html#popen -конструктору , если вы посмотрите на аргумент shell, вы найдете

аргумент оболочки (по умолчанию False) указывает, использовать ли оболочку в качестве программы для выполнения. Если shell равен True, рекомендуется передавать аргументы как строку, а не как последовательность.

Это означает, что при выполнении второй команды она запускается как echo и, следовательно, вы получаете только новая строка.

2 голосов
/ 08 января 2020

Первый аргумент subprocess.Popen() сообщает системе, что запускать.

Когда это список, вам нужно , чтобы использовать shell=False. По совпадению, это работает так, как вы надеетесь в Windows; но на Unix -подобных платформах вы просто передаете ряд аргументов, которые обычно игнорируются. Фактически,

/bin/sh -c 'echo' '$HOME'

, который просто приводит к тому, что второй аргумент не используется ни для чего (где я использую одинарные кавычки, чтобы подчеркнуть, что это просто строки c).

В моем скромное мнение, Python должно выдать ошибку в этом случае. На Windows тоже. Это ошибка, которая должна быть перехвачена и сообщена.

(В противном случае, если указано shell=False, но переданная вами строка не является именем допустимой команды, вы в конечном итоге получите ошибку в любом случае, и это имеет смысл, если у вас есть даже смутное представление о том, что происходит.)

Если вы действительно знаете, что делаете, вы можете заставить первый аргумент получить доступ к последующим аргументам; например,

/bin/sh -c 'printf "%s\n" "$@"' 'ick' 'foo' 'bar' 'baz'

напечатает foo, bar и baz в отдельных строках. (Аргумент "ноль" - здесь, 'ick' - используется для заполнения $0.) Но это всего лишь неясное следствие; не пытайтесь использовать это ни для чего.

Кроме того, вам не следует использовать subprocess.Popen(), если вы просто хотите, чтобы команда выполнялась. Документация subprocess.run() говорит вам об этом более подробно. С text=True вы получаете строку вместо байтов.

result = subprocess.run('echo "$HOME"', shell=True,
    text=True, capture_output=True, check=True)
print(result.stdout, result.stderr)

И, конечно, os.environ['HOME'] позволяет вам получить доступ к значению $HOME из Python. Это также позволяет вам избегать shell=True, что вы обычно должны, если можете.

2 голосов
/ 08 января 2020

Когда у вас есть shell=True, фактический процесс, который запускается - это процесс оболочки, т. Е. Думайте, что он работает /bin/sh -c на unix. Аргументы, которые вы передаете Попену, передаются в качестве аргументов этому процессу оболочки. Так /bin/sh -c 'echo' '$HOME' печатает перевод строки, а второй аргумент игнорируется. Поэтому обычно вы должны использовать только строковые аргументы с shell=True.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...