Python – как вызывать команды bash с помощью канала?

Я могу запустить это обычно в командной строке в Linux:

$ tar c my_dir | md5sum

Но когда я пытаюсь вызвать его с помощью Python, я получаю сообщение об ошибке:

>>> subprocess.Popen(['tar','-c','my_dir','|','md5sum'],shell=True)
<subprocess.Popen object at 0x26c0550>
>>> tar: You must specify one of the `-Acdtrux' or `--test-label'  options
Try `tar --help' or `tar --usage' for more information.

person Greg    schedule 06.09.2011    source источник
comment
Почему вы хэшируете файл tar? Вы имеете в виду искать изменения в содержимом файла? или проверить внешне созданный файл tar?   -  person tMC    schedule 06.09.2011
comment
Возможно, см. также stackoverflow.com/questions/24306205/   -  person tripleee    schedule 07.02.2021


Ответы (4)


Вы также должны использовать subprocess.PIPE для разделения команды, вы должны использовать shlex.split() для предотвращения странного поведения в некоторых случаях:

from subprocess import Popen, PIPE
from shlex import split
p1 = Popen(split("tar -c mydir"), stdout=PIPE)
p2 = Popen(split("md5sum"), stdin=p1.stdout)

Но для создания архива и генерации его контрольной суммы следует использовать встроенные модули Python tarfile и hashlib вместо вызова команд оболочки.

person mdeous    schedule 06.09.2011
comment
tarfile и hashlib предпочтительнее. Но как мне хэшировать объект tarfile? - person Greg; 06.09.2011
comment
@Greg не хэширует объект tarfile, открывает полученный файл, как и любой другой файл, используя open(), а затем хэширует его содержимое. - person mdeous; 06.09.2011
comment
Имеет смысл. Это работает, но я получаю значение хеш-функции, отличное от исходной команды. Этого следует ожидать? - person Greg; 06.09.2011
comment
@ Грег, это должно делать то же самое, что и tar -c mydir | md5sum. Возможно, вы могли бы начать новый вопрос, включая сеанс интерактивного терминала, где вы запускаете эту команду, запускаете Python и запускаете команды Python, отображая вывод. - person Mike Graham; 06.09.2011

Хорошо, я не уверен, почему, но это, кажется, работает:

subprocess.call("tar c my_dir | md5sum",shell=True)

Кто-нибудь знает, почему оригинальный код не работает?

person Greg    schedule 06.09.2011
comment
труба | это символ, который оболочка понимает для соединения входных и выходных команд вместе. Это не аргумент, который tar понимает, и не команда. Вы пытаетесь выполнить все как аргументы для команды tar, если только вы не создаете подоболочку. - person tMC; 06.09.2011
comment
Это работает, потому что вся команда передается в оболочку, а оболочка понимает |. Popen вызывает процесс и напрямую передает аргументы. Для Popen это управляется с помощью shell= и передачи строки (не списка), IIRC. - person ; 06.09.2011

На самом деле вам нужно запустить подпроцесс оболочки с командой оболочки в качестве параметра:

>>> subprocess.Popen(['sh', '-c', 'echo hi | md5sum'], stdout=subprocess.PIPE).communicate()
('764efa883dda1e11db47671c4a3bbd9e  -\n', None)
person Dag    schedule 06.09.2011

я бы попробовал ваш на python v3.8.10:

import subprocess
proc1 = subprocess.run(['tar c my_dir'], stdout=subprocess.PIPE, shell=True)
proc2 = subprocess.run(['md5sum'], input=proc1.stdout, stdout=subprocess.PIPE, shell=True)
print(proc2.stdout.decode())

ключевые моменты (например, план в моем решении на связанных https://stackoverflow.com/a/68323133/12361522):

  • subprocess.run()
  • нет разделения команды и параметров bash, т.е. ['tar c my_dir']или ["tar c my_dir"]
  • stdout=subprocess.PIPE для всех процессов
  • input=proc1.stdout цепочка выхода предыдущего на вход следующего
  • включить оболочку shell=True
person secretAgent    schedule 09.07.2021