Як перенаправити вихід з підпроцесом в Python?


97

Що я роблю в командному рядку:

cat file1 file2 file3 > myfile

Що я хочу зробити з python:

import subprocess, shlex
my_cmd = 'cat file1 file2 file3 > myfile'
args = shlex.split(my_cmd)
subprocess.call(args) # spits the output in the window i call my python program

Виконання такої команди в підпроцесі не дасть вам жодного результату. Можливо, ви захочете запустити його без > myfile, що перенаправляє вихідні дані з файлу cat file1 file2 file3 в python?
PoltoS

@PoltoS Я хочу приєднати деякі файли, а потім обробити отриманий файл. Я вважав, що використання кота - найпростіша альтернатива. Чи є кращий / пітонічний спосіб це зробити?
catatemypythoncode

os.sendfile()можливе рішення, див. Відтворення команди unix cat у python
jfs

1
Я думаю, що переспрямування виводу ('>' або '>>') не працює в підпроцесі. Popen (принаймні в Python 2.7) (у режимі shell = True) У цьому прикладі, як вказують інші, ви можете обійти це не використовуючи переспрямування, але в інших випадках переадресація корисна. Якщо переспрямування або трубопроводи не підтримуються в підпроцесі. Попен слід задокументувати (та / або os.system () не слід припиняти, поки це не буде виправлено)
Рибо,

Відповіді:


20

ОНОВЛЕННЯ: os.system не рекомендується, хоча все ще доступний у Python 3.


Використання os.system:

os.system(my_cmd)

Якщо ви дійсно хочете використовувати підпроцес, ось рішення (здебільшого зняте з документації для підпроцесу):

p = subprocess.Popen(my_cmd, shell=True)
os.waitpid(p.pid, 0)

OTOH, ви можете повністю уникнути системних дзвінків:

import shutil

with open('myfile', 'w') as outfile:
    for infile in ('file1', 'file2', 'file3'):
        shutil.copyfileobj(open(infile), outfile)

1
Це працює, але дозвольте тоді запитати вас: який сенс у бібліотеці підпроцесу, якщо os.system вже виконає роботу? Я відчуваю, що натомість мав би використовувати підпроцес, оскільки це бібліотека, присвячена цьому завданню, хоча, оскільки я роблю це лише для себе, цього разу мені буде добре використовувати os.system.
catatemypythoncode

Бібліотека підпроцесу набагато гнучкіша за os.systemта може os.systemточно моделювати , але з нею також складніше працювати.
Marcelo Cantos

13
os.systemприйшов раніше subprocess. Перший - це застарілий API, який останній має намір замінити.
Санта

5
@catatemypythoncode: ви не повинні використовувати os.system()або shell=True. Щоб перенаправити висновок підпроцесу, використовуйте stdoutпараметр, як показано у відповіді Райана Томпсона . Хоча catу вашому випадку вам не потрібен підпроцес ( ), ви можете об'єднати файли за допомогою чистого Python.
jfs

4
OTOH = З іншого боку
Цефлін

271

У Python 3.5+ для перенаправлення виводу просто передайте дескриптор відкритого файлу для stdoutаргументу subprocess.run:

# Use a list of args instead of a string
input_files = ['file1', 'file2', 'file3']
my_cmd = ['cat'] + input_files
with open('myfile', "w") as outfile:
    subprocess.run(my_cmd, stdout=outfile)

Як зазначали інші, використання зовнішньої команди, подібної catдля цієї мети, є абсолютно стороннім.


9
Це має бути відповіддю на загальне питання про трубопровід при використанні оболонки від Python
Kaushik Ghose

46
Це правильна відповідь, а не та, яка позначена як правильна.
Джастін Блейк,

7
Для Python 3.5+ використання, subprocess.run(my_cmd, stdout=outfile)яке замінює,subprocess.call(...)
Остін Йейтс,

1
Зауважимо, що це не працює з користувацькими файловими об’єктами, якщо вони не мають поля fileno (якщо вони не є справжнім файлом.)
Еліезер Мірон

1
Оскільки Python <3.5 на сьогодні застарілий, я оновив відповідь вашим коментарем @AustinYates.
Грег Дубіцький

5

@PoltoS Я хочу приєднати деякі файли, а потім обробити отриманий файл. Я думав, що використання кота є найпростішою альтернативою. Чи є кращий / пітонічний спосіб це зробити?

Звичайно:

with open('myfile', 'w') as outfile:
    for infilename in ['file1', 'file2', 'file3']:
        with open(infilename) as infile:
            outfile.write(infile.read())

1
size = 'ffprobe -v error -show_entries format=size -of default=noprint_wrappers=1:nokey=1 dump.mp4 > file'
proc = subprocess.Popen(shlex.split(size), shell=True)
time.sleep(1)
proc.terminate() #proc.kill() modify it by a suggestion
size = ""
with open('file', 'r') as infile:
    for line in infile.readlines():
        size += line.strip()

print(size)
os.remove('file')

Коли ви використовуєте підпроцес , процес потрібно вбити. Це приклад. Якщо ви не вб'єте процес, файл буде порожнім, і ви нічого не зможете прочитати. Він може працювати в Windows. Я не можу переконатися, що він може запустити на Unix.


1
Це приклад поганого коду (він не буде працювати в Unix; він демонструє погані практики for line in .readlines():, s +=) і proc.kill()може призвести до втрати інформації в цілому (він не дозволяє підпроцесу витончено завершуватися (на Unix) - розмитий вміст втрачається ). У будь-якому випадку, примітка про буферизацію доречніша як коментар.
jfs

Я запускаю це в Windows - це нормально (оскільки вбивство дорівнює завершенню в Windows). На Unix, можливо, вам слід використовувати proc.terminate (). @ JF Себастьян У мене немає системи Unix на моєму комп'ютері.
wyx

Якщо ви на Windows , то падіння shlex.split(), падіння shell=True, падіння >file, падіння open()і т.д. і використання stdout=PIPE, Timer(1, proc.terminate).start(); output = proc.communicate()[0]замість цього. Ось повний приклад . Інші рішення: Зупинити читання результатів процесу в Python без зависання? Примітка: у питанні немає вимоги, що вам потрібно припинити дочірній процес вручну - ви можете вирішити інші проблеми, наприклад, процес може поводитися інакше, якщо його stdout є tty, але це не за темою.
jfs

0

Цікавим випадком було б оновлення файлу, додавши до нього подібний файл. Тоді не потрібно було б створювати новий файл у процесі. Це особливо корисно у випадку, коли потрібно додати великий файл. Ось одна можливість використання командного рядка teminal безпосередньо з python.

import subprocess32 as sub

with open("A.csv","a") as f:
    f.flush()
    sub.Popen(["cat","temp.csv"],stdout=f)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.