เรียกใช้คำสั่งเชลล์ใน Python


65

ฉันกำลังศึกษาการทดสอบการเจาะและการเขียนโปรแกรม Python ฉันแค่อยากรู้ว่าฉันจะดำเนินการกับคำสั่ง Linux ใน Python อย่างไร คำสั่งที่ฉันต้องการดำเนินการคือ:

echo 1 > /proc/sys/net/ipv4/ip_forward
iptables -t nat -A PREROUTING -p tcp --destination-port 80 -j REDIRECT --to-port 8080

ถ้าฉันใช้printPython และรันในเทอร์มินัลมันจะเหมือนกับการใช้งานเหมือนกับว่าคุณพิมพ์ด้วยตัวเองและกดEnter?


5
os.systemสามารถทำสิ่งนี้ได้
fooot

2
และฉันคิดว่าbashเป็นเปลือกป่อง ...
mikeserv

3
เอกสารสำหรับการos.systemแนะนำให้ใช้subprocessโมดูล
จอร์แดน

คุณต้องการเอาต์พุตของ iptables หรือไม่?
คิระ

3
คำถามนี้ควรโยกย้ายไปที่ Stackoverflow
www139

คำตอบ:


106

คุณสามารถใช้os.system()เช่นนี้

import os
os.system('ls')

หรือในกรณีของคุณ:

os.system('echo 1 > /proc/sys/net/ipv4/ip_forward')
os.system('iptables -t nat -A PREROUTING -p tcp --destination-port 80 -j REDIRECT --to-port 8080')

ยิ่งไปกว่านั้นคุณสามารถใช้การเรียกใช้ของกระบวนการย่อยได้ปลอดภัยยิ่งขึ้นมีประสิทธิภาพมากขึ้นและมีแนวโน้มเร็วขึ้น:

from subprocess import call
call('echo "I like potatos"', shell=True)

หรือโดยไม่ต้องเรียกใช้เชลล์:

call(['echo', 'I like potatos'])

หากคุณต้องการจับเอาท์พุทวิธีหนึ่งในการทำเช่นนี้:

import subprocess
cmd = ['echo', 'I like potatos']
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)

o, e = proc.communicate()

print('Output: ' + o.decode('ascii'))
print('Error: '  + e.decode('ascii'))
print('code: ' + str(proc.returncode))

ฉันขอแนะนำให้ตั้งค่าtimeoutในcommunicateและจับข้อยกเว้นที่คุณจะได้รับเมื่อเรียกใช้ นี่เป็นรหัสที่ผิดพลาดได้ง่ายดังนั้นคุณควรคาดหวังว่าจะเกิดข้อผิดพลาดขึ้นและจัดการกับมัน

https://docs.python.org/3/library/subprocess.html


23
os.system เลิกใช้แล้วตั้งแต่เวอร์ชัน 2.6 กระบวนการย่อยเป็นโมดูลที่เหมาะสมที่จะใช้
binarysubstrate

@binarysubstrate เลิกใช้แล้วเนื่องจากไม่รองรับหรือไม่พร้อมใช้งาน ฉันเพิ่งทำงานกับเครื่องที่มี 2.7 (ไม่ใช่โดยทางเลือก) และos.systemยังใช้งานได้
openwonk

1
นอกจากนี้หากใช้subprocess.callตามที่แนะนำคุณอาจต้องระบุshell=True... ดูที่นี่
openwonk

ด้วย Python 3.4 เชลล์จะต้องระบุ True มิฉะนั้นคำสั่ง call จะไม่ทำงาน โดยค่าเริ่มต้นการโทรจะพยายามเปิดไฟล์ที่ระบุโดยสตริงเว้นแต่ว่า shell = True ถูกตั้งค่าไว้ มันก็ดูเหมือนว่าใน Python 3.5 การโทรจะถูกแทนที่ด้วย run
DLH

รหัส POSIX ทั่วไปอาจเรียกdecode()ด้วยชุดอักขระจากLC_CTYPEตัวแปรสภาพแวดล้อม
Mikko Rantalainen

29

คำสั่งแรกเขียนไปยังไฟล์ คุณจะไม่เรียกใช้งานในฐานะคำสั่งเชลล์เนื่องจากpythonสามารถอ่านและเขียนไฟล์โดยไม่ใช้เชลล์:

with open('/proc/sys/net/ipv4/ip_forward', 'w') as f:
    f.write("1")

iptablesคำสั่งเป็นสิ่งที่คุณอาจต้องการที่จะดำเนินการจากภายนอก วิธีที่ดีที่สุดที่จะทำเช่นนี้คือการใช้โมดูลกระบวนการย่อย

import subprocess
subprocess.check_call(['iptables', '-t', 'nat', '-A',
                       'PREROUTING', '-p', 'tcp', 
                       '--destination-port', '80',
                       '-j', 'REDIRECT', '--to-port', '8080'])

โปรดทราบว่าวิธีนี้ยังไม่ได้ใช้เปลือกซึ่งเป็นค่าใช้จ่ายที่ไม่จำเป็น


13

วิธีที่เร็วที่สุด:

import os
os.system("your command here")

นี่ไม่ใช่วิธีการที่ยืดหยุ่นที่สุด หากคุณต้องการควบคุมกระบวนการของคุณมากกว่า "เรียกใช้หนึ่งครั้งเพื่อทำให้เสร็จสมบูรณ์และบล็อกจนกว่าจะออก" คุณควรใช้subprocessโมดูลแทน


8

ตามกฎทั่วไปคุณควรใช้การผูกงูเหลือมทุกครั้งที่ทำได้ (ควรยกเว้นการจับที่ดีกว่าและข้อดีอื่น ๆ )

สำหรับechoคำสั่งเห็นได้ชัดว่าควรใช้ไพ ธ อนเพื่อเขียนลงไฟล์ตามคำแนะนำในคำตอบของ @ jordanm

สำหรับiptablesคำสั่งอาจจะpython-iptables( หน้า PyPi , หน้า GitHub กับรายละเอียดและเอกสาร ) จะให้สิ่งที่คุณต้องการ (ผมไม่ได้ตรวจสอบคำสั่งเฉพาะของคุณ)

สิ่งนี้จะทำให้คุณขึ้นอยู่กับ lib ภายนอกดังนั้นคุณต้องน้ำหนักผลประโยชน์ การใช้กระบวนการย่อยทำงานได้ แต่ถ้าคุณต้องการใช้ผลลัพธ์คุณจะต้องแยกวิเคราะห์เองและจัดการกับการเปลี่ยนแปลงผลลัพธ์ในiptablesเวอร์ชันในอนาคต


สิ่งที่ควรทราบก็คือรหัสการทดสอบหน่วยที่มีการsubprocessโทรมีประโยชน์น้อยกว่า ใช่คุณสามารถจำลองการโทรได้ แต่การทดสอบจะต้องมีข้อสมมติฐานเกี่ยวกับผลลัพธ์ของการโทรภายนอก
jordanm

คำตอบของคุณเป็นเหมือนคำแนะนำ OP ได้ถามโดยเฉพาะว่าเขาสามารถเรียกใช้คำสั่งระบบ linux จาก python ได้อย่างไร
kapad

@kapad ใช่ แต่เขายังระบุด้วยว่าทำไมเขาถึงต้องการทำเช่นนั้นและฉันเสนอทางเลือกอื่น
Jérôme

2

เปลือกงูหลามของคุณ ระวังฉันยังไม่ได้ทดสอบ

from subprocess import run

def bash(command):
        run(command.split())

>>> bash('find / -name null')
/dev/null
/sys/fs/selinux/null
/sys/devices/virtual/mem/null
/sys/class/mem/null
/usr/lib/kbd/consoletrans/null

0

หากคุณต้องการรันคำสั่งนี้ในระบบอื่นหลังจาก SSH คุณอาจต้องใช้โมดูลชื่อ Paramiko มันมีประโยชน์จริงๆ

หากการดำเนินการคำสั่งต้องทำจากเครื่องท้องถิ่นฟังก์ชั่นโมดูลระบบปฏิบัติการจะเป็นประโยชน์

หน้าแรก: https://www.paramiko.org/

การพัฒนา: https://github.com/paramiko/paramiko

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.