หากต้องการขยายคำตอบก่อนหน้านี้ให้กว้างขึ้นมีจำนวนรายละเอียดที่มักถูกมองข้าม
- ต้องการ
subprocess.run()
มากกว่าsubprocess.check_call()
และเพื่อนมากกว่าsubprocess.call()
มากกว่าsubprocess.Popen()
มากกว่าos.system()
มากกว่าos.popen()
- ทำความเข้าใจและอาจจะใช้อาคา
text=True
universal_newlines=True
- ทำความเข้าใจเกี่ยวกับความหมาย
shell=True
หรือshell=False
วิธีการเปลี่ยนแปลงการอ้างถึงและความพร้อมของสิ่งอำนวยความสะดวกของเชลล์
- ทำความเข้าใจความแตกต่างระหว่าง
sh
และ Bash
- ทำความเข้าใจว่ากระบวนการย่อยแยกจากหลักและไม่สามารถเปลี่ยนพาเรนต์ได้
- หลีกเลี่ยงการใช้ Python interpreter เป็น subprocess ของ Python
หัวข้อเหล่านี้จะกล่าวถึงในรายละเอียดเพิ่มเติมด้านล่าง
ชอบsubprocess.run()
หรือsubprocess.check_call()
subprocess.Popen()
ฟังก์ชั่นเทียมระดับต่ำ แต่มันก็เป็นเรื่องยุ่งยากที่จะใช้อย่างถูกต้องและคุณจะสิ้นสุดการคัดลอก / วางหลายบรรทัดของรหัส ... ซึ่งสิ่งอำนวยความสะดวกที่มีอยู่แล้วในห้องสมุดมาตรฐานเป็นชุดของระดับที่สูงขึ้นห่อหุ้มฟังก์ชั่นเพื่อวัตถุประสงค์ต่างๆ ซึ่งจะนำเสนอในรายละเอียดเพิ่มเติมดังต่อไปนี้
นี่คือย่อหน้าจากเอกสาร :
วิธีที่แนะนำในการเรียกใช้กระบวนการย่อยคือการใช้run()
ฟังก์ชั่นสำหรับทุกกรณีการใช้งานที่สามารถจัดการได้ สำหรับกรณีการใช้งานขั้นสูงเพิ่มเติมPopen
สามารถใช้อินเตอร์เฟสพื้นฐานได้โดยตรง
น่าเสียดายที่ความพร้อมใช้งานของฟังก์ชั่นการห่อหุ้มเหล่านี้แตกต่างกันระหว่างรุ่น Python
subprocess.run()
เปิดตัวเป็นทางการใน Python 3.5 มีวัตถุประสงค์เพื่อแทนที่ทั้งหมดต่อไปนี้
subprocess.check_output()
ได้รับการแนะนำใน Python 2.7 / 3.1 มันเป็นพื้นเทียบเท่ากับsubprocess.run(..., check=True, stdout=subprocess.PIPE).stdout
subprocess.check_call()
เปิดตัวใน Python 2.5 มันเป็นพื้นเทียบเท่ากับsubprocess.run(..., check=True)
subprocess.call()
เปิดตัวใน Python 2.4 ในsubprocess
โมดูลดั้งเดิม( PEP-324 ) มันเป็นพื้นเทียบเท่ากับsubprocess.run(...).returncode
API ระดับสูง subprocess.Popen()
การปรับโครงสร้างและการขยายเพิ่มเติมsubprocess.run()
นั้นมีเหตุผลและหลากหลายมากกว่าฟังก์ชั่นเก่าที่มันเข้ามาแทนที่ มันส่งคืนCompletedProcess
วัตถุที่มีวิธีการต่าง ๆ ที่ช่วยให้คุณสามารถเรียกคืนสถานะออกมาตรฐานผลลัพธ์และตัวบ่งชี้ผลลัพธ์และสถานะอื่น ๆ จากกระบวนการย่อยที่เสร็จสิ้นแล้ว
subprocess.run()
เป็นวิธีที่จะไปหากคุณต้องการให้โปรแกรมรันและกลับไปควบคุม Python สำหรับสถานการณ์ที่เกี่ยวข้องมากขึ้น (กระบวนการพื้นหลังบางทีด้วย I / O แบบโต้ตอบกับโปรแกรมหลัก Python) คุณยังต้องใช้subprocess.Popen()
และดูแลระบบประปาทั้งหมดด้วยตัวเอง สิ่งนี้ต้องการความเข้าใจที่ค่อนข้างซับซ้อนของชิ้นส่วนที่เคลื่อนไหวทั้งหมดและไม่ควรนำมาใช้อย่างเบามือ Popen
วัตถุที่เรียบง่ายกว่านั้นแสดงถึงกระบวนการ (อาจยังคงทำงานอยู่) ซึ่งจำเป็นต้องได้รับการจัดการจากรหัสของคุณตลอดอายุการใช้งานของกระบวนการย่อย
บางทีควรจะเน้นว่าเพียงsubprocess.Popen()
แค่สร้างกระบวนการ หากคุณปล่อยไว้ที่นั้นคุณจะมีกระบวนการย่อยทำงานพร้อมกับ Python ดังนั้นกระบวนการ "พื้นหลัง" หากไม่จำเป็นต้องป้อนข้อมูลหรือส่งออกหรือประสานงานกับคุณก็สามารถทำงานที่มีประโยชน์ควบคู่ไปกับโปรแกรม Python ของคุณ
หลีกเลี่ยงos.system()
และos.popen()
ตั้งแต่เวลานิรันดร์ (ดีตั้งแต่ Python 2.5) os
เอกสารโมดูลได้มีคำแนะนำที่ต้องการsubprocess
มากกว่าos.system()
:
subprocess
โมดูลให้สิ่งอำนวยความสะดวกที่มีประสิทธิภาพมากขึ้นสำหรับการวิ่งพล่านกระบวนการใหม่และเรียกผลของพวกเขา; การใช้โมดูลนั้นจะดีกว่าการใช้ฟังก์ชั่นนี้
ปัญหาที่เกิดขึ้นsystem()
คือมันขึ้นอยู่กับระบบอย่างชัดเจนและไม่ได้เสนอวิธีการโต้ตอบกับกระบวนการย่อย มันทำงานเพียงแค่มีเอาต์พุตมาตรฐานและข้อผิดพลาดมาตรฐานนอกเหนือจากการเข้าถึงของ Python ข้อมูลเดียวที่ Python ได้รับกลับมาคือสถานะออกของคำสั่ง (ศูนย์หมายถึงความสำเร็จแม้ว่าความหมายของค่าที่ไม่เป็นศูนย์ก็ขึ้นอยู่กับระบบด้วยเช่นกัน)
PEP-324 (ซึ่งถูกกล่าวถึงแล้วข้างต้น) มีเหตุผลที่ละเอียดกว่านี้สำหรับสาเหตุที่os.system
เป็นปัญหาและsubprocess
ความพยายามแก้ไขปัญหาเหล่านั้นอย่างไร
os.popen()
เคยเป็นกำลังใจอย่างยิ่งยิ่งขึ้น:
เลิกใช้แล้วตั้งแต่เวอร์ชัน 2.6:ฟังก์ชันนี้ล้าสมัย ใช้subprocess
โมดูล
อย่างไรก็ตามเนื่องจากใน Python 3 บางครั้งมีการปรับใช้ใหม่เพื่อใช้งานง่ายsubprocess
และเปลี่ยนเส้นทางไปยังsubprocess.Popen()
เอกสารประกอบเพื่อดูรายละเอียด
ทำความเข้าใจและมักจะใช้ check=True
คุณจะสังเกตเห็นว่าsubprocess.call()
มีข้อ จำกัดos.system()
มากมายเช่นกัน ในการใช้งานตามปกติโดยทั่วไปคุณควรตรวจสอบว่ากระบวนการเสร็จสิ้นเรียบร้อยแล้วซึ่งsubprocess.check_call()
และsubprocess.check_output()
ทำ (ที่หลังยังส่งกลับออกมาตรฐานของกระบวนการย่อยสำเร็จรูป) ในทำนองเดียวกันคุณควรใช้check=True
กับsubprocess.run()
เว้นแต่คุณจะต้องอนุญาตให้กระบวนการย่อยส่งคืนสถานะข้อผิดพลาดโดยเฉพาะ
ในทางปฏิบัติด้วยcheck=True
หรือsubprocess.check_*
Python จะส่งCalledProcessError
ข้อยกเว้นหากกระบวนการย่อยส่งคืนสถานะการออกที่ไม่ใช่ศูนย์
ข้อผิดพลาดทั่วไปที่มีsubprocess.run()
คือละเว้นcheck=True
และประหลาดใจเมื่อโค้ดดาวน์สตรีมล้มเหลวหากกระบวนการย่อยล้มเหลว
ในทางกลับกันปัญหาที่พบบ่อยกับcheck_call()
และcheck_output()
คือผู้ใช้ที่ใช้ฟังก์ชั่นเหล่านี้สุ่มสี่สุ่มห้าประหลาดใจเมื่อข้อยกเว้นถูกยกขึ้นเช่นเมื่อgrep
ไม่พบการแข่งขัน (คุณควรแทนที่grep
ด้วยรหัสไพ ธ อนต่อไปตามที่อธิบายไว้ด้านล่าง)
คุณต้องเข้าใจว่าคำสั่งเชลล์ส่งคืนโค้ดออกและภายใต้เงื่อนไขใดที่พวกเขาจะส่งคืนโค้ดออกที่ไม่ใช่ศูนย์ (ข้อผิดพลาด) และทำการตัดสินใจอย่างมีสติว่าควรจัดการอย่างไร
เข้าใจและอาจใช้text=True
อาคาuniversal_newlines=True
ตั้งแต่ Python 3 สตริงภายในของ Python เป็นสตริง Unicode แต่ไม่รับประกันว่ากระบวนการย่อยจะสร้างเอาต์พุต Unicode หรือสตริงเลย
(หากความแตกต่างไม่ชัดเจนในทันทีเราแนะนำให้ใช้ Pragmatic Unicodeของ Ned Batchelder ถ้าไม่จำเป็นต้องอ่านทันทีมีการนำเสนอวิดีโอ 36 นาทีหลังลิงค์หากคุณต้องการ แต่การอ่านหน้าตัวเองอาจใช้เวลาน้อยลงอย่างมาก )
ไพ ธ อนต้องดึงข้อมูลbytes
บัฟเฟอร์และตีความอย่างใด ถ้ามันมีข้อมูลไบนารี่จำนวนมากมันไม่ควรถูกถอดรหัสลงในสตริง Unicode เพราะนั่นเป็นพฤติกรรมที่เกิดข้อผิดพลาดและทำให้เกิดข้อผิดพลาดซึ่งเป็นพฤติกรรมที่น่ารำคาญที่ทำให้สคริปต์ Python 2 จำนวนมากถูกต้องก่อนที่จะมีวิธี แยกความแตกต่างระหว่างข้อความที่เข้ารหัสและข้อมูลไบนารีอย่างถูกต้อง
ด้วยtext=True
คุณบอกว่างูหลามที่คุณในความเป็นจริงคาดหวังว่าข้อมูลกลับต้นฉบับเดิมในการเข้ารหัสเริ่มต้นของระบบและว่ามันควรจะถอดรหัสเป็นงูหลาม (Unicode) สตริงที่ดีที่สุดของความสามารถของงูใหญ่ (ปกติ UTF-8 ที่ใด ๆ ในระดับปานกลางขึ้นไป ระบบวันที่ยกเว้น Windows)
ถ้านั่นไม่ใช่สิ่งที่คุณร้องขอกลับ Python จะให้bytes
สตริงกับคุณstdout
และstderr
สตริง บางทีในบางจุดหลังจากนั้นคุณจะรู้ว่าพวกเขาเป็นสตริงข้อความหลังจากทั้งหมดและคุณจะรู้ว่าการเข้ารหัสของพวกเขา จากนั้นคุณสามารถถอดรหัสได้
normal = subprocess.run([external, arg],
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
check=True,
text=True)
print(normal.stdout)
convoluted = subprocess.run([external, arg],
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
check=True)
# You have to know (or guess) the encoding
print(convoluted.stdout.decode('utf-8'))
งูหลาม 3.7 แนะนำนามแฝงสั้นและอธิบายเพิ่มเติมและเข้าใจสำหรับการโต้แย้งคำหลักซึ่งก่อนหน้านี้ค่อนข้างเรียกว่าทำให้เข้าใจผิดtext
universal_newlines
ทำความเข้าใจshell=True
กับshell=False
เมื่อshell=True
คุณส่งสตริงเดียวไปยังเชลล์ของคุณและเชลล์นำมาจากที่นั่น
เมื่อshell=False
คุณส่งรายการอาร์กิวเมนต์ไปยัง OS ให้ข้ามเชลล์
เมื่อคุณไม่มีเชลล์คุณจะบันทึกกระบวนการและกำจัดความซับซ้อนที่ซ่อนอยู่ซึ่งค่อนข้างมากซึ่งอาจหรืออาจจะไม่ได้ดักจับข้อบกพร่องหรือแม้แต่ปัญหาด้านความปลอดภัย
ในทางกลับกันเมื่อคุณไม่มีเชลล์คุณไม่มีการเปลี่ยนเส้นทางการขยายสัญลักษณ์การควบคุมงานและคุณลักษณะเชลล์อื่น ๆ จำนวนมาก
ข้อผิดพลาดทั่วไปคือการใช้shell=True
แล้วยังส่ง Python รายการโทเค็นหรือในทางกลับกัน สิ่งนี้เกิดขึ้นกับการทำงานในบางกรณี แต่ไม่ชัดเจนและอาจแตกต่างกันในวิธีที่น่าสนใจ
# XXX AVOID THIS BUG
buggy = subprocess.run('dig +short stackoverflow.com')
# XXX AVOID THIS BUG TOO
broken = subprocess.run(['dig', '+short', 'stackoverflow.com'],
shell=True)
# XXX DEFINITELY AVOID THIS
pathological = subprocess.run(['dig +short stackoverflow.com'],
shell=True)
correct = subprocess.run(['dig', '+short', 'stackoverflow.com'],
# Probably don't forget these, too
check=True, text=True)
# XXX Probably better avoid shell=True
# but this is nominally correct
fixed_but_fugly = subprocess.run('dig +short stackoverflow.com',
shell=True,
# Probably don't forget these, too
check=True, text=True)
การตอบโต้ทั่วไป "แต่ใช้งานได้สำหรับฉัน" ไม่ใช่ข้อโต้แย้งที่มีประโยชน์เว้นแต่คุณจะเข้าใจอย่างถ่องแท้ว่าสถานการณ์จะหยุดทำงานได้อย่างไร
ตัวอย่างการสร้างใหม่
บ่อยครั้งที่คุณสมบัติของเชลล์สามารถถูกแทนที่ด้วยโค้ด Python ดั้งเดิม ง่าย ๆ หรือsed
สคริปต์ควรแปลเป็น Python แทน
เพื่อแสดงให้เห็นบางส่วนนี่คือตัวอย่างทั่วไป แต่โง่เล็กน้อยซึ่งเกี่ยวข้องกับคุณลักษณะเชลล์มากมาย
cmd = '''while read -r x;
do ping -c 3 "$x" | grep 'round-trip min/avg/max'
done <hosts.txt'''
# Trivial but horrible
results = subprocess.run(
cmd, shell=True, universal_newlines=True, check=True)
print(results.stdout)
# Reimplement with shell=False
with open('hosts.txt') as hosts:
for host in hosts:
host = host.rstrip('\n') # drop newline
ping = subprocess.run(
['ping', '-c', '3', host],
text=True,
stdout=subprocess.PIPE,
check=True)
for line in ping.stdout.split('\n'):
if 'round-trip min/avg/max' in line:
print('{}: {}'.format(host, line))
บางสิ่งที่ควรทราบที่นี่:
- ด้วย
shell=False
คุณไม่จำเป็นต้องมีข้อความที่เชลล์ต้องการรอบสตริง การใส่เครื่องหมายคำพูดต่อไปอาจเป็นข้อผิดพลาด
- มันมักจะเหมาะสมที่จะเรียกใช้โค้ดน้อยที่สุดใน subprocess สิ่งนี้ช่วยให้คุณควบคุมการดำเนินการจากภายในโค้ด Python ของคุณได้มากขึ้น
- ต้องบอกว่าท่อเปลือกที่ซับซ้อนนั้นน่าเบื่อและบางครั้งก็มีความท้าทายในการนำมาใช้ใหม่ใน Python
รหัส refactored ยังแสดงให้เห็นว่าเชลล์มีประโยชน์ต่อคุณมากเพียงใดด้วยซินแท็กซ์ที่สั้นมาก - ดีขึ้นหรือแย่ลง Python กล่าวอย่างชัดเจนดีกว่าโดยนัยแต่รหัส Python คือค่อนข้างละเอียดและเนื้อหาที่มีลักษณะที่ซับซ้อนมากขึ้นกว่านี้จริงๆ ในทางกลับกันมันมีหลายจุดที่คุณสามารถควบคุมได้ตรงกลางของสิ่งอื่น ๆ ดังที่ยกตัวอย่างเล็กน้อยโดยการปรับปรุงที่เราสามารถรวมชื่อโฮสต์พร้อมกับเอาต์พุตคำสั่งเชลล์ได้อย่างง่ายดาย (สิ่งนี้ไม่ได้เป็นการท้าทายที่จะทำในเชลล์เช่นกัน แต่ด้วยค่าใช้จ่ายในการเบี่ยงเบนความสนใจและอาจเป็นกระบวนการอื่น)
โครงสร้างเชลล์สามัญ
เพื่อความสมบูรณ์นี่คือคำอธิบายสั้น ๆ ของคุณสมบัติเชลล์เหล่านี้บางส่วนและหมายเหตุบางประการเกี่ยวกับวิธีที่พวกเขาสามารถถูกแทนที่ด้วยอุปกรณ์อำนวยความสะดวกดั้งเดิมของ Python
- globbing ขยายตัวสัญลักษณ์แทน aka สามารถถูกแทนที่ด้วยหรือมากมักจะมีการเปรียบเทียบสตริงหลามง่ายๆเช่น
glob.glob()
for file in os.listdir('.'): if not file.endswith('.png'): continue
Bash มีสิ่งอำนวยความสะดวกการขยายอื่น ๆ เช่น.{png,jpg}
การขยายรั้งและการขยายตัว{1..100}
หนอน ( ~
ขยายไปยังโฮมไดเร็กตอรี่ของคุณ, และโดยทั่วไป~account
ไปยังโฮมไดเร็กตอรี่ของผู้ใช้รายอื่น)
- ตัวแปรเชลล์เช่น
$SHELL
หรือ$my_exported_var
บางครั้งสามารถถูกแทนที่ด้วยตัวแปร Python ตัวแปรเปลือกส่งออกมีอยู่เป็นเช่นos.environ['SHELL']
(ความหมายของการexport
ที่จะทำให้ตัวแปรที่มีให้กระบวนการย่อย -. ตัวแปรที่ไม่สามารถใช้ได้กับกระบวนการย่อยจะเห็นได้ชัดว่าไม่สามารถใช้ได้กับงูหลามทำงานตามกระบวนการย่อยของเปลือกหรือในทางกลับกันการให้env=
คำหลัก การโต้แย้งกับsubprocess
วิธีการช่วยให้คุณสามารถกำหนดสภาพแวดล้อมของกระบวนการย่อยเป็นพจนานุกรมดังนั้นจึงเป็นวิธีหนึ่งที่จะทำให้ตัวแปร Python สามารถมองเห็นได้ในกระบวนการย่อย) ด้วยshell=False
คุณจะต้องเข้าใจวิธีการลบคำพูดใด ๆ ; ตัวอย่างเช่นcd "$HOME"
เทียบเท่าos.chdir(os.environ['HOME'])
ไม่มีเครื่องหมายคำพูดล้อมรอบชื่อไดเรกทอรี (บ่อยมากcd
ไม่มีประโยชน์หรือจำเป็นต่อไปและผู้เริ่มต้นหลายคนไม่ใส่เครื่องหมายคำพูดคู่รอบ ๆ ตัวแปรและออกไปจนกว่าจะถึงหนึ่งวัน ... )
- การเปลี่ยนเส้นทางช่วยให้คุณอ่านจากไฟล์เป็นอินพุตมาตรฐานของคุณและเขียนเอาต์พุตมาตรฐานของคุณไปยังไฟล์
grep 'foo' <inputfile >outputfile
เปิดoutputfile
สำหรับการเขียนและinputfile
สำหรับการอ่านและส่งผ่านเนื้อหาเป็นอินพุตมาตรฐานไปgrep
ที่ซึ่งเอาต์พุตมาตรฐานจะตกลงoutputfile
มา ซึ่งโดยทั่วไปจะไม่ยากที่จะแทนที่ด้วยรหัส Python ดั้งเดิม
- ท่อเป็นรูปแบบของการเปลี่ยนเส้นทาง
echo foo | nl
รันสองกระบวนการย่อยโดยที่เอาต์พุตมาตรฐานของecho
คืออินพุตมาตรฐานของnl
(บนระดับระบบปฏิบัติการในระบบ Unix-like นี้เป็นตัวจัดการไฟล์เดียว) ถ้าคุณไม่สามารถแทนที่หนึ่งหรือทั้งสองด้านของท่อด้วยรหัสหลามพื้นเมืองอาจจะคิดเกี่ยวกับการใช้เปลือกหลังจากทั้งหมดโดยเฉพาะอย่างยิ่งถ้าท่อมีมากกว่าสองหรือสามกระบวนการ ( แต่ดูที่pipes
โมดูลในห้องสมุดมาตรฐานงูใหญ่หรือจำนวน ของคู่แข่งบุคคลที่สามที่ทันสมัยและหลากหลายมากขึ้น)
- การควบคุมงานช่วยให้คุณสามารถขัดจังหวะงาน, เรียกใช้งานในพื้นหลัง, ส่งคืนงานเบื้องหน้า, ฯลฯ สัญญาณ Unix ขั้นพื้นฐานเพื่อหยุดและดำเนินการกระบวนการต่อได้แน่นอนจาก Python เช่นกัน แต่งานเป็นสิ่งที่เป็นนามธรรมในระดับที่สูงขึ้นในเชลล์ซึ่งเกี่ยวข้องกับกลุ่มกระบวนการ ฯลฯ ซึ่งคุณต้องเข้าใจถ้าคุณต้องการทำสิ่งนี้จาก Python
- การอ้างถึงเชลล์อาจทำให้คุณสับสนจนกว่าคุณจะเข้าใจว่าทุกอย่างนั้นเป็นสตริง ดังนั้นจึง
ls -l /
เทียบเท่ากับ'ls' '-l' '/'
แต่การอ้างอิงรอบตัวอักษรนั้นเป็นตัวเลือกที่สมบูรณ์ สตริงที่ไม่ได้ใส่เครื่องหมายคำพูดที่มีอักขระเชลล์ตัวอักขระผ่านการขยายพารามิเตอร์การทำโทเค็นช่องว่างและการขยายสัญลักษณ์ เครื่องหมายอัญประกาศคู่ป้องกันการโทเค็นช่องว่างและการขยาย wildcard แต่อนุญาตให้มีการขยายพารามิเตอร์ (การทดแทนตัวแปรการทดแทนคำสั่งและการประมวลผลแบ็กสแลช) นี่เป็นทฤษฎีง่ายๆ แต่สามารถทำให้สับสนโดยเฉพาะอย่างยิ่งเมื่อมีการตีความหลายชั้น (เช่นคำสั่งเชลล์ระยะไกล)
ทำความเข้าใจความแตกต่างระหว่างsh
และ Bash
subprocess
รันคำสั่งเชลล์ของคุณด้วย/bin/sh
เว้นแต่คุณจะร้องขอเป็นอย่างอื่นโดยเฉพาะ (ยกเว้นแน่นอนบน Windows ซึ่งมันใช้ค่าของCOMSPEC
ตัวแปร) ซึ่งหมายความว่าคุณสมบัติต่างๆของ Bash-only เท่านั้นเช่นอาร์เรย์[[
ฯลฯจะไม่สามารถใช้งานได้
หากคุณต้องการใช้ไวยากรณ์ของ Bash เท่านั้นคุณสามารถส่งผ่านพา ธ ไปที่เชลล์เป็นexecutable='/bin/bash'
(ซึ่งแน่นอนว่าถ้าติดตั้ง Bash ของคุณไว้ที่อื่นคุณต้องปรับพา ธ )
subprocess.run('''
# This for loop syntax is Bash only
for((i=1;i<=$#;i++)); do
# Arrays are Bash-only
array[i]+=123
done''',
shell=True, check=True,
executable='/bin/bash')
A subprocess
แยกต่างหากจากพาเรนต์และไม่สามารถเปลี่ยนได้
ข้อผิดพลาดที่ค่อนข้างบ่อยคือทำอะไรบางอย่างเช่น
subprocess.run('foo=bar', shell=True)
subprocess.run('echo "$foo"', shell=True) # Doesn't work
ซึ่งนอกเหนือจากการขาดความสง่างามยังทรยศต่อการขาดพื้นฐานของความเข้าใจในส่วน "ย่อย" ของชื่อ "subprocess"
กระบวนการลูกทำงานแยกจาก Python อย่างสมบูรณ์และเมื่อดำเนินการเสร็จ Python ไม่ทราบว่ามันทำอะไร (นอกเหนือจากตัวบ่งชี้ที่คลุมเครือว่ามันสามารถอนุมานจากสถานะทางออกและผลลัพธ์จากกระบวนการลูก) โดยทั่วไปเด็กไม่สามารถเปลี่ยนสภาพแวดล้อมของผู้ปกครองได้ ไม่สามารถตั้งค่าตัวแปรเปลี่ยนไดเร็กตอรี่การทำงานหรือสื่อสารกับผู้ปกครองได้หลายคำโดยไม่ได้รับความร่วมมือจากผู้ปกครอง
การแก้ไขทันทีในกรณีนี้คือการรันทั้งสองคำสั่งในกระบวนการย่อยเดียว
subprocess.run('foo=bar; echo "$foo"', shell=True)
แม้ว่าจะเห็นได้ชัดว่ากรณีการใช้งานเฉพาะนี้ไม่จำเป็นต้องใช้เชลล์เลย จำไว้ว่าคุณสามารถจัดการสภาพแวดล้อมของกระบวนการปัจจุบัน (และทำให้ลูกของมัน) ผ่านทาง
os.environ['foo'] = 'bar'
หรือส่งการตั้งค่าสภาพแวดล้อมไปยังกระบวนการลูกด้วย
subprocess.run('echo "$foo"', shell=True, env={'foo': 'bar'})
(ไม่ต้องพูดถึง refactoring ที่เห็นได้ชัดsubprocess.run(['echo', 'bar'])
แต่echo
เป็นตัวอย่างที่ดีของบางสิ่งบางอย่างที่จะเรียกใช้ในกระบวนการย่อยในตอนแรกแน่นอน)
อย่าเรียกใช้ Python จาก Python
นี่เป็นคำแนะนำที่น่าสงสัยเล็กน้อย มีสถานการณ์ที่เหมาะสมหรือเป็นความต้องการที่แน่นอนในการเรียกใช้ Python interpreter เป็น subprocess จากสคริปต์ Python แต่บ่อยครั้งมากวิธีการที่ถูกต้องคือเพียงแค่import
โมดูล Python อื่น ๆ ลงในสคริปต์การโทรของคุณและเรียกใช้ฟังก์ชันโดยตรง
ถ้าสคริปต์ Python อื่น ๆ ที่อยู่ภายใต้การควบคุมของคุณและจะไม่โมดูลให้พิจารณาเปลี่ยนมันเป็นหนึ่ง (คำตอบนี้ยาวเกินไปแล้วดังนั้นฉันจะไม่เจาะลึกรายละเอียดที่นี่)
หากคุณต้องการความเท่าเทียมคุณสามารถเรียกใช้ฟังก์ชั่น Python ในกระบวนการย่อยด้วยmultiprocessing
โมดูล นอกจากนี้ยังมีการthreading
เรียกใช้งานหลายงานในกระบวนการเดียว (ซึ่งมีน้ำหนักเบากว่าและให้การควบคุมมากขึ้น แต่ยังมีข้อ จำกัด มากขึ้นในเธรดภายในกระบวนการที่มีการเชื่อมโยงอย่างแน่นหนาและผูกพันกับGILเดียว)
cwm
ดูเหมือนจะมีความแตกต่างกันในสภาพแวดล้อมขึ้นอยู่กับวิธีการที่คุณทำงาน บางทีคุณอาจมีการกำหนดค่าบางอย่างในการ.bashrc
ตั้งค่าสภาพแวดล้อมสำหรับการใช้งานทุบตีเชิงโต้ตอบ