คุณเรียกคำสั่งภายนอกได้อย่างไร (ราวกับว่าฉันพิมพ์ที่ Unix shell หรือ Windows command prompt) จากภายในสคริปต์ Python
คุณเรียกคำสั่งภายนอกได้อย่างไร (ราวกับว่าฉันพิมพ์ที่ Unix shell หรือ Windows command prompt) จากภายในสคริปต์ Python
คำตอบ:
ดูโมดูลย่อยในไลบรารีมาตรฐาน:
import subprocess
subprocess.run(["ls", "-l"])
ประโยชน์ของการsubprocess
เทียบsystem
ก็คือว่ามันมีความยืดหยุ่นมากขึ้น (คุณจะได้รับstdout
, stderr
ที่ "จริง" รหัสสถานะจัดการข้อผิดพลาดที่ดีขึ้น ฯลฯ ... )
เอกสารอย่างเป็นทางการแนะนำsubprocess
โมดูลมากกว่าทางเลือกos.system()
:
subprocess
โมดูลให้สิ่งอำนวยความสะดวกที่มีประสิทธิภาพมากขึ้นสำหรับการวิ่งพล่านกระบวนการใหม่และเรียกผลของพวกเขา; การใช้โมดูลนั้นดีกว่าการใช้ฟังก์ชั่นนี้ [os.system()
]
การแทนที่ฟังก์ชั่นที่เก่ากว่าด้วยส่วนโมดูลย่อยในsubprocess
เอกสารอาจมีสูตรที่มีประโยชน์
สำหรับเวอร์ชันของ Python ก่อน 3.5 ให้ใช้call
:
import subprocess
subprocess.call(["ls", "-l"])
shell=True
เพื่อการทำงาน
shell=True
เพื่อจุดประสงค์นี้งูใหญ่มาพร้อมกับos.path.expandvars os.path.expandvars("$PATH")
ในกรณีของคุณคุณสามารถเขียน: @SethMMorton โปรดพิจารณาความคิดเห็นของคุณอีกครั้ง -> ทำไมไม่ใช้ shell = True
for
วงฉันจะทำอย่างไรโดยไม่ได้บล็อกสคริปต์หลามของฉัน ฉันไม่สนใจเกี่ยวกับผลลัพธ์ของคำสั่งที่ฉันต้องการเรียกใช้จำนวนมาก
subprocess
เวลาshell=False
ได้จากนั้นใช้shlex.split
วิธีง่ายๆในการทำเอกสารนี้docs.python.org/2/library/shlex.html#shlex.split
นี่คือบทสรุปของวิธีเรียกโปรแกรมภายนอกและข้อดีและข้อเสียของแต่ละโปรแกรม:
os.system("some_command with args")
ส่งคำสั่งและอาร์กิวเมนต์ไปยังเชลล์ของระบบของคุณ นี่เป็นสิ่งที่ดีเพราะคุณสามารถเรียกใช้หลายคำสั่งพร้อมกันในลักษณะนี้และตั้งค่าไพพ์และการเปลี่ยนทิศทางอินพุต / เอาต์พุต ตัวอย่างเช่น:
os.system("some_command < input_file | another_command > output_file")
อย่างไรก็ตามในขณะที่สะดวกคุณต้องจัดการการหลบหนีของอักขระเชลล์เช่นช่องว่าง ฯลฯ ด้วยตนเองในทางกลับกันสิ่งนี้ยังช่วยให้คุณเรียกใช้คำสั่งที่เป็นเพียงคำสั่งเชลล์และไม่ใช่โปรแกรมภายนอกจริง ๆ ดูเอกสาร
stream = os.popen("some_command with args")
จะทำสิ่งเดียวกันos.system
ยกเว้นว่าจะให้วัตถุเหมือนไฟล์ที่คุณสามารถใช้ในการเข้าถึงอินพุต / เอาต์พุตมาตรฐานสำหรับกระบวนการนั้น มีอีกสามสายพันธุ์ของต้นกำเนิดที่จัดการ i / o แตกต่างกันเล็กน้อย หากคุณส่งทุกอย่างเป็นสตริงคำสั่งของคุณจะถูกส่งไปยังเชลล์ หากคุณผ่านรายการเหล่านั้นเป็นรายการคุณไม่ต้องกังวลเกี่ยวกับการหลบหนีอะไรเลย ดูเอกสาร
Popen
ระดับของsubprocess
โมดูล สิ่งนี้มีจุดประสงค์เพื่อทดแทนos.popen
แต่มีข้อเสียของความซับซ้อนเล็กน้อยโดยอาศัยความครอบคลุมอย่างมาก ตัวอย่างเช่นคุณจะพูดว่า:
print subprocess.Popen("echo Hello World", shell=True, stdout=subprocess.PIPE).stdout.read()
แทน:
print os.popen("echo Hello World").read()
แต่มันก็ดีที่มีตัวเลือกทั้งหมดในคลาสหนึ่งเดียวแทนฟังก์ชัน popen ที่แตกต่างกัน 4 ตัว ดูเอกสาร
call
ฟังก์ชั่นจากsubprocess
โมดูล นี่เป็นเหมือนPopen
คลาสและรับอาร์กิวเมนต์เดียวกันทั้งหมด แต่ก็รอจนกว่าคำสั่งจะเสร็จสมบูรณ์และให้โค้ดส่งคืนแก่คุณ ตัวอย่างเช่น:
return_code = subprocess.call("echo Hello World", shell=True)
หากคุณใช้ Python 3.5 หรือใหม่กว่าคุณสามารถใช้subprocess.run
ฟังก์ชั่นใหม่ซึ่งมีลักษณะเหมือนข้างบน แต่มีความยืดหยุ่นมากกว่าและส่งคืนCompletedProcess
วัตถุเมื่อคำสั่งดำเนินการเสร็จ
โมดูลระบบปฏิบัติการมีฟังก์ชั่น fork / exec / spawn ทั้งหมดที่คุณมีในโปรแกรม C แต่ฉันไม่แนะนำให้ใช้โดยตรง
subprocess
โมดูลอาจจะเป็นสิ่งที่คุณใช้
ในที่สุดโปรดทราบว่าสำหรับวิธีการทั้งหมดที่คุณส่งคำสั่งสุดท้ายที่จะดำเนินการโดยเปลือกเป็นสตริงและคุณมีความรับผิดชอบในการหลบหนี มีผลกระทบด้านความปลอดภัยที่ร้ายแรงหากส่วนใดส่วนหนึ่งของสตริงที่คุณผ่านไม่สามารถเชื่อถือได้อย่างสมบูรณ์ ตัวอย่างเช่นหากผู้ใช้ป้อนบางส่วนของสตริง หากคุณไม่แน่ใจให้ใช้วิธีการเหล่านี้กับค่าคงที่เท่านั้น หากต้องการให้คำแนะนำเกี่ยวกับนัยให้พิจารณารหัสนี้:
print subprocess.Popen("echo %s " % user_input, stdout=PIPE).stdout.read()
และจินตนาการว่าผู้ใช้ป้อนบางสิ่ง "แม่ของฉันไม่ได้รักฉัน && rm -rf /" ซึ่งสามารถลบระบบไฟล์ทั้งหมดได้
open
ใช้กลับเห็บหรือ
subprocess.run()
ขึ้นไปใช้ docs.python.org/3.5/library/subprocess.html#subprocess.run
subprocess.run(..)
สิ่งที่ทำ"สิ่งนี้ไม่ได้จับ stdout หรือ stderr โดยค่าเริ่มต้น" บ่งบอก? แล้วเรื่องsubprocess.check_output(..)
STDERR ล่ะ?
echo
ด้านหน้าของสตริงที่ส่งผ่านไปยังPopen
? echo my mama didnt love me && rm -rf /
ดังนั้นคำสั่งเต็มรูปแบบจะจะ
subprocess.run()
หรือพี่น้องรุ่นเก่าsubprocess.check_call()
และคณะ subprocess.Popen()
สำหรับกรณีที่เหล่านี้ไม่พอเพียงให้ดู os.popen()
ไม่ควรพูดถึงเลยหรือมาหลังจาก "แฮกรหัส fork / exec / spawn ของคุณเอง"
การใช้งานทั่วไป:
import subprocess
p = subprocess.Popen('ls', shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
for line in p.stdout.readlines():
print line,
retval = p.wait()
คุณมีอิสระที่จะทำสิ่งที่คุณต้องการด้วยstdout
ข้อมูลในไปป์ ในความเป็นจริงคุณก็สามารถละเว้นพารามิเตอร์เหล่านั้น ( stdout=
และstderr=
) os.system()
และมันจะทำตัวเหมือน
.readlines()
อ่านทุกบรรทัดพร้อมกันนั่นคือมันจะบล็อกจนกว่าจะออกจากกระบวนการย่อย (ปิดท้ายของไปป์) หากต้องการอ่านแบบเรียลไทม์ (หากไม่มีปัญหาการบัฟเฟอร์) คุณสามารถ:for line in iter(p.stdout.readline, ''): print line,
p.stdout.readline()
(หมายเหตุ: ไม่มีs
ที่ท้าย) จะไม่เห็นข้อมูลใด ๆ จนกว่าเด็กจะเติมบัฟเฟอร์ หากเด็กไม่ได้สร้างข้อมูลจำนวนมากผลลัพธ์จะไม่เป็นแบบเรียลไทม์ ดูเหตุผลที่สองในQ: ทำไมไม่ใช้เพียงไพพ์ (popen ())? . การแก้ปัญหาบางอย่างมีให้ในคำตอบนี้ (pexpect, pty, stdbuf)
Popen
งานง่าย ๆอีกต่อไป shell=True
นอกจากนี้ยังไม่จำเป็นต้องระบุ ลองsubprocess.run()
คำตอบหนึ่งข้อ
คำแนะนำบางอย่างเกี่ยวกับการแยกกระบวนการลูกออกจากกระบวนการเรียก (เริ่มกระบวนการเด็กเป็นพื้นหลัง)
สมมติว่าคุณต้องการเริ่มงานที่ยาวนานจากสคริปต์ CGI นั่นคือกระบวนการลูกควรอยู่ได้นานกว่ากระบวนการเรียกใช้สคริปต์ CGI
ตัวอย่างแบบคลาสสิกจากเอกสารประกอบโมดูลย่อยคือ:
import subprocess
import sys
# Some code here
pid = subprocess.Popen([sys.executable, "longtask.py"]) # Call subprocess
# Some more code here
แนวคิดในที่นี้คือคุณไม่ต้องการรอใน 'กระบวนการย่อยการโทร' ของบรรทัดจนกว่า longtask.py จะเสร็จสิ้น แต่ไม่ชัดเจนว่าเกิดอะไรขึ้นหลังจากบรรทัด 'โค้ดเพิ่มเติมที่นี่' จากตัวอย่าง
แพลตฟอร์มเป้าหมายของฉันคือ FreeBSD แต่การพัฒนาอยู่บน Windows ดังนั้นฉันต้องเผชิญกับปัญหาบน Windows ก่อน
บน Windows (Windows XP) กระบวนการหลักจะไม่เสร็จสิ้นจนกว่า longtask.py จะทำงานเสร็จ ไม่ใช่สิ่งที่คุณต้องการในสคริปต์ CGI ปัญหาไม่ได้ระบุเฉพาะกับ Python ในชุมชน PHP ปัญหานั้นเหมือนกัน
การแก้ปัญหาคือการส่ง DETACHED_PROCESS กระบวนการสร้างค่าสถานะไปยังฟังก์ชัน CreateProcess พื้นฐานใน Windows API หากคุณติดตั้ง pywin32 คุณสามารถนำเข้าการตั้งค่าสถานะจากโมดูล win32process มิฉะนั้นคุณควรกำหนดด้วยตัวคุณเอง:
DETACHED_PROCESS = 0x00000008
pid = subprocess.Popen([sys.executable, "longtask.py"],
creationflags=DETACHED_PROCESS).pid
/ * UPD 2015.10.27 @eryksun ในความคิดเห็นด้านล่างหมายเหตุว่าการตั้งค่าสถานะที่ถูกต้องในเชิงความหมายคือ CREATE_NEW_CONSOLE (0x00000010) * /
ใน FreeBSD เรามีปัญหาอื่น: เมื่อกระบวนการหลักเสร็จสิ้นกระบวนการลูกก็จะเสร็จสิ้นเช่นกัน และนั่นไม่ใช่สิ่งที่คุณต้องการในสคริปต์ CGI เช่นกัน การทดลองบางอย่างแสดงให้เห็นว่าปัญหาดูเหมือนจะแบ่งปัน sys.stdout และวิธีแก้ปัญหาการทำงานมีดังต่อไปนี้:
pid = subprocess.Popen([sys.executable, "longtask.py"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
ฉันไม่ได้ตรวจสอบรหัสบนแพลตฟอร์มอื่น ๆ และไม่ทราบสาเหตุของพฤติกรรมใน FreeBSD หากใครรู้กรุณาแบ่งปันความคิดของคุณ Googling เกี่ยวกับกระบวนการเริ่มต้นพื้นหลังใน Python ยังไม่ได้ลดแสงใด ๆ
DETACHED_PROCESS
ในการcreationflags
หลีกเลี่ยงการนี้โดยการป้องกันเด็กจากการสืบทอดหรือการสร้างคอนโซล หากคุณต้องการคอนโซลใหม่ให้ใช้CREATE_NEW_CONSOLE
(0x00000010)
os.devnull
เพราะโปรแกรมคอนโซลบางตัวจบการทำงานโดยมีข้อผิดพลาดเป็นอย่างอื่น สร้างคอนโซลใหม่เมื่อคุณต้องการให้กระบวนการลูกโต้ตอบกับผู้ใช้พร้อมกับกระบวนการหลัก มันจะทำให้สับสนในการพยายามทำทั้งสองอย่างในหน้าต่างเดียว
import os
os.system("your command")
โปรดทราบว่านี่เป็นอันตรายเนื่องจากคำสั่งไม่ได้รับการทำความสะอาด ฉันปล่อยให้คุณไปที่ google สำหรับเอกสารที่เกี่ยวข้องในโมดูล 'os' และ 'sys' มีฟังก์ชั่นมากมาย (exec * และ spawn *) ที่จะทำสิ่งที่คล้ายกัน
subprocess
ที่โซลูชันอเนกประสงค์และพกพาเล็กน้อย แน่นอนว่าการรันคำสั่งภายนอกนั้นเป็นสิ่งที่ไม่สามารถทำได้อย่างแน่นอน (คุณต้องแน่ใจว่าคำสั่งนั้นมีอยู่ในทุกสถาปัตยกรรมที่คุณต้องการสนับสนุน) และส่งผ่านอินพุตของผู้ใช้เนื่องจากคำสั่งภายนอกนั้นไม่ปลอดภัย
ฉันขอแนะนำให้ใช้โมดูลsubprocessแทนที่จะเป็น os.system เพราะมันช่วยให้คุณรอดพ้นจากเชลล์และปลอดภัยกว่ามาก
subprocess.call(['ping', 'localhost'])
subprocess
เวลาshell=False
ได้จากนั้นใช้shlex.split
วิธีง่าย ๆ ในการทำเอกสารนี้docs.python.org/2/library/shlex.html#shlex.split ( เป็นวิธีที่แนะนำตาม docs docs.python.org/2/library/subprocess.html#popen-constructor ) ซึ่งเป็นวิธีที่แนะนำ)
import os
cmd = 'ls -al'
os.system(cmd)
os.popen
หากคุณต้องการที่จะกลับผลลัพธ์ของคำสั่งที่คุณสามารถใช้ อย่างไรก็ตามสิ่งนี้เลิกใช้แล้วตั้งแต่เวอร์ชัน 2.6 เพื่อสนับสนุนโมดูลย่อยซึ่งคำตอบอื่น ๆ ครอบคลุมได้ดี
มีไลบรารีที่แตกต่างกันมากมายซึ่งให้คุณโทรไปยังคำสั่งภายนอกด้วย Python สำหรับแต่ละห้องสมุดฉันได้รับคำอธิบายและแสดงตัวอย่างของการเรียกคำสั่งภายนอก คำสั่งที่ฉันใช้เป็นตัวอย่างคือls -l
(รายการไฟล์ทั้งหมด) หากคุณต้องการทราบข้อมูลเพิ่มเติมเกี่ยวกับห้องสมุดใด ๆ ที่ฉันได้ทำรายการไว้และเชื่อมโยงเอกสารประกอบสำหรับแต่ละห้องสมุด
แหล่งที่มา:
นี่คือห้องสมุดทั้งหมด:
หวังว่านี่จะช่วยคุณในการตัดสินใจว่าจะใช้ไลบรารีใด :)
กระบวนการย่อย
กระบวนการย่อยช่วยให้คุณสามารถเรียกใช้คำสั่งภายนอกและเชื่อมต่อกับอินพุต / เอาต์พุต / ข้อผิดพลาดของไปป์ (stdin, stdout และ stderr) กระบวนการย่อยเป็นตัวเลือกเริ่มต้นสำหรับการเรียกใช้คำสั่ง แต่บางครั้งโมดูลอื่น ๆ จะดีกว่า
subprocess.run(["ls", "-l"]) # Run command
subprocess.run(["ls", "-l"], stdout=subprocess.PIPE) # This will run the command and return any output
subprocess.run(shlex.split("ls -l")) # You can also use the shlex library to split the command
ระบบปฏิบัติการ
ระบบปฏิบัติการใช้สำหรับ "ฟังก์ชันการทำงานของระบบปฏิบัติการที่พึ่งพา" นอกจากนี้ยังสามารถใช้เรียกคำสั่งภายนอกด้วยos.system
และos.popen
(หมายเหตุ: นอกจากนี้ยังมี subprocess.popen) subprocess.run
ระบบปฏิบัติการจะใช้เปลือกและเป็นทางเลือกที่ง่ายสำหรับคนที่ไม่จำเป็นต้องหรือไม่ทราบวิธีการใช้งาน
os.system("ls -l") # run command
os.popen("ls -l").read() # This will run the command and return any output
ดวลจุดโทษ
sh เป็นส่วนต่อประสานย่อยของกระบวนการซึ่งให้คุณสามารถเรียกโปรแกรมเหมือนกับว่าเป็นฟังก์ชัน สิ่งนี้มีประโยชน์หากคุณต้องการรันคำสั่งหลายครั้ง
sh.ls("-l") # Run command normally
ls_cmd = sh.Command("ls") # Save command as a variable
ls_cmd() # Run command as if it were a function
plumbum
plumbum เป็นห้องสมุดสำหรับโปรแกรม Python แบบสคริปต์ คุณสามารถเรียกโปรแกรมเช่นฟังก์ชั่นsh
ได้ Plumbum มีประโยชน์ถ้าคุณต้องการเรียกใช้ไพพ์ไลน์โดยไม่มีเชลล์
ls_cmd = plumbum.local("ls -l") # get command
ls_cmd() # run command
pexpect
pexpect ช่วยให้คุณวางไข่แอปพลิเคชันสำหรับเด็กควบคุมและค้นหารูปแบบในผลลัพธ์ได้ นี่เป็นทางเลือกที่ดีกว่าสำหรับกระบวนการย่อยสำหรับคำสั่งที่คาดว่าจะมี tty บน Unix
pexpect.run("ls -l") # Run command as normal
child = pexpect.spawn('scp foo user@example.com:.') # Spawns child application
child.expect('Password:') # When this is the output
child.sendline('mypassword')
ผ้า
fabric เป็นไลบรารี Python 2.5 และ 2.7 มันช่วยให้คุณสามารถรันคำสั่งโลคัลและรีโมตเชลล์ Fabric เป็นทางเลือกง่าย ๆ สำหรับการรันคำสั่งใน secure shell (SSH)
fabric.operations.local('ls -l') # Run command as normal
fabric.operations.local('ls -l', capture = True) # Run command and receive output
ราชทูต
นักการทูตเรียกว่า "subprocess for human" มันถูกใช้เป็นเครื่องห่อหุ้มสิ่งอำนวยความสะดวกรอบ ๆsubprocess
โมดูล
r = envoy.run("ls -l") # Run command
r.std_out # get output
คำสั่ง
commands
มีฟังก์ชั่น wrapper สำหรับos.popen
แต่มันถูกลบออกจาก Python 3 เนื่องจากsubprocess
เป็นทางเลือกที่ดีกว่า
การแก้ไขขึ้นอยู่กับความคิดเห็นของ JF Sebastian
ฉันมักจะใช้fabric
สิ่งนี้เช่น:
from fabric.operations import local
result = local('ls', capture=True)
print "Content:/n%s" % (result, )
แต่ตอนนี้ดูเหมือนจะเป็นเครื่องมือที่ดี: (อินเตอร์เฟซหลามกระบวนการย่อย)sh
ดูตัวอย่าง:
from sh import vgdisplay
print vgdisplay()
print vgdisplay('-v')
print vgdisplay(v=True)
ตรวจสอบคลัง Python "pexpect" ด้วย
จะช่วยให้การควบคุมแบบโต้ตอบของโปรแกรม / คำสั่งภายนอกแม้ ssh, ftp, telnet ฯลฯ คุณสามารถพิมพ์สิ่งที่ชอบ:
child = pexpect.spawn('ftp 192.168.0.24')
child.expect('(?i)name .*: ')
child.sendline('anonymous')
child.expect('(?i)password')
ใช้โมดูลย่อย (Python 3):
import subprocess
subprocess.run(['ls', '-l'])
เป็นวิธีมาตรฐานที่แนะนำ อย่างไรก็ตามงานที่ซับซ้อนมากขึ้น (ไปป์, เอาท์พุต, อินพุทและอื่น ๆ ) นั้นน่าเบื่อในการสร้างและเขียน
หมายเหตุเกี่ยวกับเวอร์ชั่น Python: หากคุณยังใช้ Python 2 อยู่subprocess.call ก็จะทำงานในลักษณะเดียวกัน
protip: shlex.splitช่วยให้คุณสามารถที่จะแยกคำสั่งสำหรับrun
, call
และอื่น ๆ ที่subprocess
ฟังก์ชั่นในกรณีที่คุณไม่ต้องการ (หรือคุณไม่ได้!) ให้พวกเขาในรูปแบบของรายการ:
import shlex
import subprocess
subprocess.run(shlex.split('ls -l'))
หากคุณไม่สนใจการพึ่งพาจากภายนอกให้ใช้plumbum :
from plumbum.cmd import ifconfig
print(ifconfig['wlan0']())
มันเป็นsubprocess
เสื้อคลุมที่ดีที่สุด มันเป็นแพลตฟอร์มข้ามคือมันทำงานได้ทั้งในระบบ Windows และ Unix เหมือน pip install plumbum
ติดตั้งโดย
อีกห้องสมุดยอดนิยมคือsh :
from sh import ifconfig
print(ifconfig('wlan0'))
อย่างไรก็ตามsh
การสนับสนุน Windows ลดลงดังนั้นจึงไม่น่าประทับใจเท่าที่เคยเป็นมา pip install sh
ติดตั้งโดย
หากคุณต้องการเอาต์พุตจากคำสั่งที่คุณเรียกใช้คุณสามารถใช้subprocess.check_output (Python 2.7+)
>>> subprocess.check_output(["ls", "-l", "/dev/null"])
'crw-rw-rw- 1 root root 1, 3 Oct 18 2007 /dev/null\n'
ให้สังเกตพารามิเตอร์ของเชลล์ด้วย
หากเชลล์คือ
True
คำสั่งที่ระบุจะถูกดำเนินการผ่านเชลล์ สิ่งนี้จะมีประโยชน์หากคุณใช้ Python เป็นหลักในการควบคุมการไหลที่ปรับปรุงให้เหนือเชลล์ระบบส่วนใหญ่และยังต้องการความสะดวกในการเข้าถึงคุณสมบัติเชลล์อื่น ๆ เช่นเชลล์ท่อ, สัญลักษณ์แทนชื่อไฟล์, การขยายตัวแปรสภาพแวดล้อมและการขยาย ~ ถึงบ้านของผู้ใช้ ไดเรกทอรี อย่างไรก็ตามทราบว่างูหลามตัวเองมีการใช้งานของคุณสมบัติเปลือกเหมือนหลายคน (โดยเฉพาะอย่างยิ่งglob
,fnmatch
,os.walk()
,os.path.expandvars()
,os.path.expanduser()
และshutil
)
check_output
ต้องมีรายการมากกว่าสตริง subprocess.check_output("ls -l /dev/null".split())
หากคุณไม่ได้ขึ้นอยู่กับพื้นที่ที่ยกมาเพื่อให้การโทรของคุณถูกต้องที่ง่ายวิธีที่สามารถอ่านได้มากที่สุดที่จะทำคือ
นี่คือวิธีที่ฉันเรียกใช้คำสั่งของฉัน รหัสนี้มีทุกสิ่งที่คุณต้องการมาก
from subprocess import Popen, PIPE
cmd = "ls -l ~/"
p = Popen(cmd , shell=True, stdout=PIPE, stderr=PIPE)
out, err = p.communicate()
print "Return code: ", p.returncode
print out.rstrip(), err.rstrip()
subprocess.run
เป็นวิธีที่แนะนำใน Python 3.5หากรหัสของคุณไม่จำเป็นต้องรักษาความเข้ากันได้กับ Python เวอร์ชันก่อนหน้า มันมีความสอดคล้องมากกว่าและให้ความสะดวกในการใช้งานคล้ายกับ Envoy (การวางท่อนั้นไม่ตรงไปตรงมาดูคำถามนี้เพื่อดูว่า )
นี่คือตัวอย่างจากเอกสารเอกสาร
เรียกใช้กระบวนการ:
>>> subprocess.run(["ls", "-l"]) # Doesn't capture output
CompletedProcess(args=['ls', '-l'], returncode=0)
เพิ่มในการทำงานล้มเหลว:
>>> subprocess.run("exit 1", shell=True, check=True)
Traceback (most recent call last):
...
subprocess.CalledProcessError: Command 'exit 1' returned non-zero exit status 1
จับเอาท์พุท:
>>> subprocess.run(["ls", "-l", "/dev/null"], stdout=subprocess.PIPE)
CompletedProcess(args=['ls', '-l', '/dev/null'], returncode=0,
stdout=b'crw-rw-rw- 1 root root 1, 3 Jan 23 16:23 /dev/null\n')
ผมขอแนะนำให้พยายามทูต มันเป็นเสื้อคลุมสำหรับกระบวนการย่อยซึ่งจะมีวัตถุประสงค์เพื่อแทนที่โมดูลและฟังก์ชั่นที่เก่ากว่า ทูตเป็นกระบวนการย่อยสำหรับมนุษย์
ตัวอย่างการใช้งานจากREADME :
>>> r = envoy.run('git config', data='data to pipe in', timeout=2)
>>> r.status_code
129
>>> r.std_out
'usage: git config [options]'
>>> r.std_err
''
ท่อไปรอบ ๆ เกินไป:
>>> r = envoy.run('uptime | pbcopy')
>>> r.command
'pbcopy'
>>> r.status_code
0
>>> r.history
[<Response 'uptime'>]
การเรียกคำสั่งภายนอกใน Python
ใช้ง่ายsubprocess.run
ซึ่งส่งคืนCompletedProcess
วัตถุ:
>>> import subprocess
>>> completed_process = subprocess.run('python --version')
Python 3.6.1 :: Anaconda 4.4.0 (64-bit)
>>> completed_process
CompletedProcess(args='python --version', returncode=0)
ตั้งแต่ Python 3.5 เอกสารแนะนำsubprocess.run :
วิธีที่แนะนำในการเรียกใช้กระบวนการย่อยคือการใช้ฟังก์ชั่น run () สำหรับทุกกรณีการใช้งานที่สามารถจัดการได้ สำหรับกรณีการใช้งานขั้นสูงเพิ่มเติมสามารถใช้อินเตอร์เฟส Popen พื้นฐานได้โดยตรง
นี่คือตัวอย่างของการใช้งานที่ง่ายที่สุดเท่าที่จะเป็นไปได้
>>> import subprocess
>>> completed_process = subprocess.run('python --version')
Python 3.6.1 :: Anaconda 4.4.0 (64-bit)
>>> completed_process
CompletedProcess(args='python --version', returncode=0)
run
รอให้คำสั่งเสร็จสิ้นเป็นผลสำเร็จจากนั้นส่งคืนCompletedProcess
วัตถุ มันอาจเพิ่มTimeoutExpired
(ถ้าคุณให้มันtimeout=
โต้เถียง) หรือCalledProcessError
(ถ้ามันล้มเหลวและคุณผ่านcheck=True
)
ในขณะที่คุณอาจอนุมานจากตัวอย่างข้างต้น stdout และ stderr ทั้งสองได้รับการไพพ์ไปยัง stdout ของคุณเองและ stderr โดยค่าเริ่มต้น
เราสามารถตรวจสอบวัตถุที่ส่งคืนและดูคำสั่งที่ได้รับและ returncode:
>>> completed_process.args
'python --version'
>>> completed_process.returncode
0
หากคุณต้องการจับเอาท์พุทคุณสามารถส่งผ่านsubprocess.PIPE
ไปยังที่เหมาะสมstderr
หรือstdout
:
>>> cp = subprocess.run('python --version',
stderr=subprocess.PIPE,
stdout=subprocess.PIPE)
>>> cp.stderr
b'Python 3.6.1 :: Anaconda 4.4.0 (64-bit)\r\n'
>>> cp.stdout
b''
(ฉันคิดว่ามันน่าสนใจและขัดข้องเล็กน้อยที่ข้อมูลรุ่นถูกนำไปใช้กับ stderr แทนที่จะเป็น stdout)
หนึ่งอาจย้ายจากการให้สตริงคำสั่งด้วยตนเอง (เช่นคำถามแนะนำ) เพื่อจัดทำสตริงที่สร้างโดยทางโปรแกรม อย่าสร้างสตริงโดยทางโปรแกรม นี่เป็นปัญหาด้านความปลอดภัยที่อาจเกิดขึ้น มันจะดีกว่าถ้าคุณไม่เชื่อถืออินพุต
>>> import textwrap
>>> args = ['python', textwrap.__file__]
>>> cp = subprocess.run(args, stdout=subprocess.PIPE)
>>> cp.stdout
b'Hello there.\r\n This is indented.\r\n'
หมายเหตุเท่านั้น args
ควรส่งผ่านตำแหน่งเท่านั้น
นี่คือลายเซ็นจริงในแหล่งที่มาและแสดงโดยhelp(run)
:
def run(*popenargs, input=None, timeout=None, check=False, **kwargs):
popenargs
และkwargs
จะได้รับกับPopen
คอนสตรัค input
สามารถเป็นสตริงไบต์ (หรือ unicode หากระบุการเข้ารหัสหรือuniversal_newlines=True
) ที่จะถูกไพพ์ไปยัง stdin ของกระบวนการย่อย
เอกสารอธิบายtimeout=
และcheck=True
ดีกว่าที่ฉันสามารถ:
อาร์กิวเมนต์การหมดเวลาถูกส่งผ่านไปยัง Popen.communicate () หากการหมดเวลาหมดอายุกระบวนการลูกจะถูกฆ่าและรอ ข้อยกเว้น TimeoutExpired จะถูกยกขึ้นอีกครั้งหลังจากกระบวนการลูกสิ้นสุดลง
หากการตรวจสอบเป็นจริงและกระบวนการออกจากด้วยรหัสทางออกที่ไม่ใช่ศูนย์จะมีการยกข้อยกเว้น CalledProcessError คุณลักษณะของข้อยกเว้นนั้นจะเก็บอาร์กิวเมนต์รหัสการออกและ stdout และ stderr หากถูกจับ
และตัวอย่างcheck=True
นี้ดีกว่าอย่างใดอย่างหนึ่งที่ฉันสามารถหา:
>>> subprocess.run("exit 1", shell=True, check=True) Traceback (most recent call last): ... subprocess.CalledProcessError: Command 'exit 1' returned non-zero exit status 1
นี่คือลายเซ็นที่ขยายตามที่กำหนดในเอกสาร:
subprocess.run(args, *, stdin=None, input=None, stdout=None, stderr=None, shell=False, cwd=None, timeout=None, check=False, encoding=None, errors=None)
โปรดทราบว่าสิ่งนี้บ่งชี้ว่ารายการ args เท่านั้นที่ควรส่งผ่านตำแหน่ง ดังนั้นให้ส่งอาร์กิวเมนต์ที่เหลือเป็นอาร์กิวเมนต์ของคำหลัก
เมื่อใช้Popen
แทน ฉันจะต่อสู้เพื่อหากรณีการใช้งานตามข้อโต้แย้งเพียงอย่างเดียว อย่างไรก็ตามการใช้งานโดยตรงPopen
จะช่วยให้คุณสามารถเข้าถึงวิธีการรวมถึงpoll
'send_signal', 'ยุติ' และ 'รอ'
นี่คือPopen
ลายเซ็นตามที่กำหนดในแหล่งที่มา ฉันคิดว่านี่คือการห่อหุ้มข้อมูลที่แม่นยำที่สุด (ตรงข้ามกับhelp(Popen)
):
def __init__(self, args, bufsize=-1, executable=None,
stdin=None, stdout=None, stderr=None,
preexec_fn=None, close_fds=_PLATFORM_DEFAULT_CLOSE_FDS,
shell=False, cwd=None, env=None, universal_newlines=False,
startupinfo=None, creationflags=0,
restore_signals=True, start_new_session=False,
pass_fds=(), *, encoding=None, errors=None):
แต่ได้ข้อมูลมากขึ้นคือเอกสาร :Popen
subprocess.Popen(args, bufsize=-1, executable=None, stdin=None, stdout=None, stderr=None, preexec_fn=None, close_fds=True, shell=False, cwd=None, env=None, universal_newlines=False, startupinfo=None, creationflags=0, restore_signals=True, start_new_session=False, pass_fds=(), *, encoding=None, errors=None)
ดำเนินการโปรแกรมย่อยในกระบวนการใหม่ บน POSIX คลาสใช้พฤติกรรม os.execvp () - like เพื่อเรียกใช้งานโปรแกรมลูก บน Windows คลาสใช้ฟังก์ชัน Windows CreateProcess () อาร์กิวเมนต์ของ Popen มีดังนี้
การทำความเข้าใจกับเอกสารที่เหลืออยู่Popen
จะเป็นแบบฝึกหัดสำหรับผู้อ่าน
shell=True
หรือ (ดีกว่า) ส่งคำสั่งเป็นรายการ
os.system
ก็โอเค แต่วันที่ประเภท มันยังไม่ปลอดภัยมาก subprocess
แต่พยายาม subprocess
ไม่เรียก sh โดยตรงและดังนั้นจึงปลอดภัยกว่าos.system
ไม่เรียกดวลจุดโทษโดยตรงและดังนั้นจึงมีความปลอดภัยมากกว่า
subprocess
ไม่ได้ลบปัญหาด้านความปลอดภัยทั้งหมดและมีปัญหาที่น่ารำคาญของตัวเอง
นอกจากนี้ยังมีPlumbum
>>> from plumbum import local
>>> ls = local["ls"]
>>> ls
LocalCommand(<LocalPath /bin/ls>)
>>> ls()
u'build.py\ndist\ndocs\nLICENSE\nplumbum\nREADME.rst\nsetup.py\ntests\ntodo.txt\n'
>>> notepad = local["c:\\windows\\notepad.exe"]
>>> notepad() # Notepad window pops up
u'' # Notepad window is closed by user, command returns
ใช้:
import os
cmd = 'ls -al'
os.system(cmd)
os - โมดูลนี้มีวิธีพกพาในการใช้ฟังก์ชั่นการทำงานของระบบปฏิบัติการ
สำหรับos
ฟังก์ชั่นอื่น ๆ เพิ่มเติมนี่คือเอกสารประกอบ
มันอาจเป็นเรื่องง่าย:
import os
cmd = "your command"
os.system(cmd)
os.system
subprocess
ฉันค่อนข้างชอบshell_commandสำหรับความเรียบง่าย มันสร้างขึ้นที่ด้านบนของโมดูลย่อย
นี่คือตัวอย่างจากเอกสาร:
>>> from shell_command import shell_call
>>> shell_call("ls *.py")
setup.py shell_command.py test_shell_command.py
0
>>> shell_call("ls -l *.py")
-rw-r--r-- 1 ncoghlan ncoghlan 391 2011-12-11 12:07 setup.py
-rw-r--r-- 1 ncoghlan ncoghlan 7855 2011-12-11 16:16 shell_command.py
-rwxr-xr-x 1 ncoghlan ncoghlan 8463 2011-12-11 16:17 test_shell_command.py
0
มีความแตกต่างอื่นที่นี่ซึ่งไม่ได้กล่าวถึงก่อนหน้านี้
subprocess.Popen
ดำเนินการ <command> เป็นกระบวนการย่อย ในกรณีของฉันฉันต้องเรียกใช้ไฟล์ <a> ที่ต้องการสื่อสารกับโปรแกรมอื่น <b>
ฉันลองกระบวนการย่อยและการดำเนินการเสร็จสมบูรณ์ อย่างไรก็ตาม <b> ไม่สามารถสื่อสารกับ <a> ทุกอย่างเป็นปกติเมื่อฉันเรียกใช้ทั้งจากสถานี
อีกหนึ่ง: (หมายเหตุ: kwrite ทำงานแตกต่างจากแอปพลิเคชันอื่น ๆ หากคุณลองด้านล่างด้วย Firefox ผลลัพธ์จะไม่เหมือนกัน)
หากคุณลองos.system("kwrite")
โฟลว์ของโปรแกรมค้างจนกว่าผู้ใช้จะปิด kwrite เพื่อเอาชนะว่าฉันพยายามแทนos.system(konsole -e kwrite)
ที่จะเอาชนะที่ฉันพยายามแทนโปรแกรมในครั้งนี้ยังคงดำเนินต่อไป แต่ kwrite กลายเป็น subprocess ของคอนโซล
ใครก็ตามที่รัน kwrite ไม่ได้เป็น subprocess (เช่นในการตรวจสอบระบบจะต้องปรากฏที่ขอบซ้ายสุดของแผนผัง)
os.system
ไม่ได้ช่วยให้คุณสามารถเก็บผลดังนั้นหากคุณต้องการผลการจัดเก็บในรายการบางส่วนหรือบางสิ่งบางอย่างที่มีsubprocess.call
ผลงาน
subprocess.check_call
สะดวกถ้าคุณไม่ต้องการทดสอบค่าส่งคืน มันจะโยนข้อยกเว้นในข้อผิดพลาดใด ๆ
ฉันมักจะใช้subprocesssร่วมกับshlex (เพื่อจัดการกับการหลบหนีของสตริงที่ยกมา):
>>> import subprocess, shlex
>>> command = 'ls -l "/your/path/with spaces/"'
>>> call_params = shlex.split(command)
>>> print call_params
["ls", "-l", "/your/path/with spaces/"]
>>> subprocess.call(call_params)
ปลั๊กไร้ยางอายฉันเขียนห้องสมุดสำหรับสิ่งนี้: P https://github.com/houqp/shell.py
ตอนนี้มันเป็นเสื้อคลุมสำหรับ popen และ shlex นอกจากนี้ยังรองรับคำสั่ง piping เพื่อให้คุณสามารถโยงคำสั่งได้ง่ายขึ้นใน Python ดังนั้นคุณสามารถทำสิ่งต่าง ๆ เช่น:
ex('echo hello shell.py') | "awk '{print $2}'"
ใน Windows คุณก็สามารถนำเข้าsubprocess
โมดูลและเรียกใช้คำสั่งภายนอกโดยการโทรsubprocess.Popen()
, subprocess.Popen().communicate()
และsubprocess.Popen().wait()
เป็นด้านล่าง:
# Python script to run a command line
import subprocess
def execute(cmd):
"""
Purpose : To execute a command and return exit status
Argument : cmd - command to execute
Return : exit_code
"""
process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
(result, error) = process.communicate()
rc = process.wait()
if rc != 0:
print "Error: failed to execute command:", cmd
print error
return result
# def
command = "tasklist | grep python"
print "This process detail: \n", execute(command)
เอาท์พุท:
This process detail:
python.exe 604 RDP-Tcp#0 4 5,660 K
ภายใต้ Linux ในกรณีที่คุณต้องการเรียกคำสั่งภายนอกที่จะดำเนินการอย่างอิสระ (จะยังคงทำงานหลังจากสคริปต์ python สิ้นสุดลง) คุณสามารถใช้คิวแบบง่าย ๆ เป็นตัวจัดคิวงานหรือคำสั่งat
ตัวอย่างที่มีตัวจัดคิวงาน:
import os
os.system('ts <your-command>')
หมายเหตุเกี่ยวกับตัวจัดคิวงาน ( ts
):
คุณสามารถตั้งค่าจำนวนกระบวนการที่เกิดขึ้นพร้อมกันให้รัน ("ช่อง") ด้วย:
ts -S <number-of-slots>
การติดตั้งts
ไม่ต้องการสิทธิ์ผู้ดูแลระบบ คุณสามารถดาวน์โหลดและรวบรวมจากแหล่งที่มาง่ายๆเพียงmake
เพิ่มไปยังเส้นทางของคุณและคุณเสร็จแล้ว
ts
ไม่ได้เป็นมาตรฐานสำหรับ distro ใด ๆ ที่ฉันรู้ถึงแม้ว่าตัวชี้ไปยังat
มีประโยชน์เล็กน้อย คุณควรพูดถึงbatch
เช่นกัน อย่างอื่นos.system()
ควรแนะนำอย่างน้อยพูดถึงนั่นsubprocess
คือการแนะนำแทน
คุณสามารถใช้ Popen จากนั้นคุณสามารถตรวจสอบสถานะของขั้นตอน:
from subprocess import Popen
proc = Popen(['ls', '-l'])
if proc.poll() is None:
proc.kill()
ตรวจสอบกระบวนการย่อยเปิด
ในการดึงรหัสเครือข่ายจากOpenStack Neutron :
#!/usr/bin/python
import os
netid = "nova net-list | awk '/ External / { print $2 }'"
temp = os.popen(netid).read() /* Here temp also contains new line (\n) */
networkId = temp.rstrip()
print(networkId)
ผลลัพธ์ของnova net-list
+--------------------------------------+------------+------+
| ID | Label | CIDR |
+--------------------------------------+------------+------+
| 431c9014-5b5d-4b51-a357-66020ffbb123 | test1 | None |
| 27a74fcd-37c0-4789-9414-9531b7e3f126 | External | None |
| 5a2712e9-70dc-4b0e-9281-17e02f4684c9 | management | None |
| 7aa697f5-0e60-4c15-b4cc-9cb659698512 | Internal | None |
+--------------------------------------+------------+------+
ผลลัพธ์ของการพิมพ์ (networkId)
27a74fcd-37c0-4789-9414-9531b7e3f126
os.popen()
ในปี 2559 สคริปต์ Awk สามารถถูกแทนที่ได้อย่างง่ายดายด้วยรหัส Python ดั้งเดิม
echo $PATH
โดยใช้call(["echo", "$PATH"])
แต่มันแค่สะท้อนสตริงตัวอักษร$PATH
แทนที่จะทำการทดแทนใด ๆ ฉันรู้ว่าฉันสามารถรับตัวแปรสภาพแวดล้อม PATH ได้ แต่ฉันสงสัยว่ามีวิธีที่ง่ายในการให้คำสั่งทำงานอย่างถูกต้องเหมือนกับว่าฉันเรียกใช้งานด้วยการทุบตี