Почему команда и ее аргументы должны быть в списке для subprocess.Popen? - PullRequest
0 голосов
/ 17 октября 2019

Я пытался выполнить

import subprocess
p = subprocess.Popen("ls -la /etc", stdout=subprocess.PIPE, stderr=subprocess.PIPE)
p.stdout.read().decode()

, что дает мне

FileNotFoundError: [Errno 2] No such file or directory: 'ls -la /etc': 'ls -la /etc'

После

Подпроцесс Python. Открыть с помощью var / args

Я сделал

import subprocess
p = subprocess.Popen(["ls", "-la", "/etc"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
p.stdout.read().decode()

Что сработало.

Почему это так? Почему я должен разделить мою команду и ее аргументы? В чем причина этого дизайна?


Версия Python:

3.7.3 (default, Mar 27 2019, 22:11:17)
[GCC 7.3.0]

Ответы [ 2 ]

2 голосов
/ 18 октября 2019

Вот как все вызовы процессов работают в UNIX.

Под капотом запуск программы в UNIX традиционно выполняется с помощью следующих шагов:

  1. fork() отключить дочерний процесс.
  2. В этом дочернем процессе откройте новые копии stdin, stdout, stderr и т. Д., Если запрашиваются перенаправления, используя dup2() вызов, чтобы назначить вновь открытые файлы дескрипторам файла, которые являются целями перенаправления.
  3. В этом дочернем процессе используйте системный вызов execve(), чтобы заменить текущий процесс желаемымдочерний процесс. Этот системный вызов принимает массив аргументов, а не одну строку.
  4. wait() для выхода дочернего элемента, если предполагается, что вызов блокируется.

Итак, subprocess.Popen предоставляет интерфейс массива, , потому что интерфейс массива - это то, что операционная система фактически делает под капотом .

Когда вы запускаете ls /tmp в оболочке эта оболочка преобразует строку в массив, а затем выполняет вышеописанные шаги сама - но это дает вам больше контроля (и позволяет избежать серьезных ошибок - если кто-то создает файл с именем /tmp/$(rm -rf ~), вы не хотите пытаться cat /tmp/$(rm -rf ~) удалить ваш домашний каталог), когда вы делаете преобразования самостоятельно.

0 голосов
/ 17 октября 2019

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

Аргументы должны быть последовательностью программных аргументов или же одиночной строкой или объектом, подобным пути. По умолчанию программа для выполнения является первым элементом в args, если args - последовательность. Если args является строкой, интерпретация зависит от платформы и описана ниже. См. Shell и исполняемые аргументы для дополнительных отличий от поведения по умолчанию. Если не указано иное, рекомендуется передавать аргументы в виде последовательности.

В POSIX, если аргумент является строкой, строка интерпретируется как имя или путь к программе, которую нужно выполнить. Однако это можно сделать только в том случае, если в программу не переданы аргументы.

Далее ...

В POSIX с shell = True оболочкапо умолчанию это / bin / sh. Если args является строкой, строка определяет команду для выполнения через оболочку. Это означает, что строка должна быть отформатирована точно так же, как при вводе в командной строке. Это включает, например, кавычки или обратную косую черту, экранирующие имена файлов с пробелами в них. Если args - последовательность, первый элемент задает командную строку, и любые дополнительные элементы будут обрабатываться как дополнительные аргументы самой оболочки. То есть Popen делает эквивалент:

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

В Windows с shell = True переменная среды COMSPEC задает оболочку по умолчанию. Единственный раз, когда вам нужно указать shell = True в Windows, это когда команда, которую вы хотите выполнить, встроена в оболочку (например, dir или copy). Вам не нужно shell = True для запуска командного файла или исполняемого файла на консоли.

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