วิธีเปลี่ยนทิศทางเอาต์พุตด้วยกระบวนการย่อยใน 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 ฉันต้องการเข้าร่วมไฟล์บางไฟล์จากนั้นประมวลผลไฟล์ที่ได้ ฉันคิดว่าการใช้แมวเป็นทางเลือกที่ง่ายที่สุด มีวิธีที่ดีกว่า / pythonic ที่จะทำหรือไม่?
catatemypythoncode

os.sendfile()วิธีแก้ปัญหาที่เป็นไปได้โปรดดูสร้างคำสั่ง unix cat ใน python
jfs

1
ฉันคิดว่าการเปลี่ยนเส้นทางเอาต์พุต ('>' หรือ '>>') ไม่ทำงานในกระบวนการย่อยเปิด (อย่างน้อยใน Python 2.7) (ในโหมด shell = True) ในตัวอย่างนี้ตามที่คนอื่นชี้ให้เห็นคุณสามารถแก้ไขได้ โดยไม่ใช้การเปลี่ยนเส้นทาง แต่ในกรณีอื่น ๆ การเปลี่ยนเส้นทางจะมีประโยชน์ หากไม่รองรับการเปลี่ยนเส้นทางหรือการวางท่อในกระบวนการย่อยควรจัดทำเอกสารopenไว้ (และ / หรือ os.system () ไม่ควรเลิกใช้งานจนกว่าจะได้รับการแก้ไข)
Ribo

คำตอบ:


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 = ในทางกลับกัน
Cephlin

279

ใน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

47
นี่คือคำตอบที่ถูกต้องไม่ใช่คำตอบที่ทำเครื่องหมายว่าถูกต้อง
Justin Blake

7
สำหรับการใช้งาน Python 3.5+ subprocess.run(my_cmd, stdout=outfile)ซึ่งจะเข้ามาแทนที่subprocess.call(...)
Austin Yates

1
สิ่งนี้ใช้ไม่ได้กับอ็อบเจ็กต์ไฟล์ที่กำหนดเองหากไม่มีฟิลด์ fileno (หากไม่ใช่ไฟล์จริง)
Eliezer Miron

1
เนื่องจาก Python <3.5 เลิกใช้งานแล้วในตอนนี้ฉันจึงอัปเดตคำตอบพร้อมกับความคิดเห็นของคุณ @AustinYates
Greg Dubicki

5

@PoltoS ฉันต้องการเข้าร่วมไฟล์บางไฟล์จากนั้นประมวลผลไฟล์ที่ได้ ฉันคิดว่าการใช้แมวเป็นทางเลือกที่ง่ายที่สุด มีวิธีที่ดีกว่า / pythonic ที่จะทำหรือไม่?

แน่นอน:

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
มันเป็นตัวอย่างที่ไม่ดีรหัส (มันจะไม่ทำงานบนระบบปฏิบัติการยูนิกซ์มันแสดงให้เห็นถึงการปฏิบัติที่ไม่ดีfor line in .readlines():, s +=) และproc.kill()อาจนำไปสู่การสูญเสียข้อมูลทั่วไป (ไม่อนุญาตให้กระบวนการย่อยที่จะยุติได้อย่างสง่างาม (บน Unix) - เนื้อหา unflushed จะหายไป ). อย่างไรก็ตามหมายเหตุเกี่ยวกับการบัฟเฟอร์มีความเหมาะสมมากกว่าสำหรับความคิดเห็น
jfs

ฉันรันบน Windows ก็โอเค (เพราะ kill เท่ากับยุติบน Windows) ใน Unix คุณควรใช้ proc.terminate () @ JF Sebastian ฉันไม่มีระบบ 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)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.