Bagaimana Anda memanggil perintah eksternal (seolah-olah saya mengetiknya di shell Unix atau command prompt Windows) dari dalam skrip Python?
4886
Lihatlah modul subproses di perpustakaan standar:
import subprocess
subprocess.run(["ls", "-l"])
Keuntungan dari subprocess
vs system
adalah bahwa hal itu lebih fleksibel (Anda bisa mendapatkan stdout
, stderr
, "nyata" kode status, penanganan kesalahan yang lebih baik, dll ...).
The dokumentasi resmi merekomendasikan subprocess
modul atas alternatif os.system()
:
The
subprocess
Modul menyediakan fasilitas yang lebih kuat untuk pemijahan proses baru dan mengambil hasil mereka; menggunakan modul itu lebih baik daripada menggunakan fungsi ini [os.system()
].
Bagian Mengganti Fungsi yang Lebih Lama dengan bagian Modul subproses dalam subprocess
dokumentasi mungkin memiliki beberapa resep yang bermanfaat.
Untuk versi Python sebelum 3.5, gunakan call
:
import subprocess
subprocess.call(["ls", "-l"])
echo $PATH
dengan menggunakancall(["echo", "$PATH"])
, tetapi itu hanya menggemakan string literal$PATH
daripada melakukan substitusi apa pun. Saya tahu saya bisa mendapatkan variabel lingkungan PATH, tapi saya bertanya-tanya apakah ada cara mudah untuk memiliki perintah berperilaku persis seolah-olah saya telah mengeksekusi di bash.shell=True
agar bisa berfungsi.shell=True
, untuk tujuan ini Python datang dengan os.path.expandvars . Dalam kasus Anda, Anda dapat menulis:os.path.expandvars("$PATH")
. @SethMMorton harap pertimbangkan kembali komentar Anda -> Mengapa tidak menggunakan shell = Truefor
lingkaran, bagaimana cara melakukannya tanpa memblokir skrip python saya? Saya tidak peduli dengan output dari perintah saya hanya ingin menjalankan banyak dari mereka.subprocess
kapanshell=False
, maka gunakanshlex.split
cara mudah untuk melakukan ini docs.python.org/2/library/shlex.html#shlex.splitBerikut ini ringkasan cara memanggil program eksternal serta kelebihan dan kekurangan masing-masing:
os.system("some_command with args")
meneruskan perintah dan argumen ke shell sistem Anda. Ini bagus karena Anda benar-benar dapat menjalankan banyak perintah sekaligus dengan cara ini dan mengatur pipa dan pengalihan input / output. Sebagai contoh:Namun, sementara ini mudah, Anda harus secara manual menangani keluarnya karakter shell seperti spasi, dll. Di sisi lain, ini juga memungkinkan Anda menjalankan perintah yang hanya perintah shell dan bukan program eksternal sebenarnya. Lihat dokumentasi .
stream = os.popen("some_command with args")
akan melakukan hal yang sama sepertios.system
kecuali bahwa itu memberi Anda objek seperti file yang dapat Anda gunakan untuk mengakses input / output standar untuk proses itu. Ada 3 varian popen lainnya yang semuanya menangani i / o sedikit berbeda. Jika Anda melewatkan semuanya sebagai string, maka perintah Anda diteruskan ke shell; jika Anda melewati mereka sebagai daftar maka Anda tidak perlu khawatir akan melarikan diri apa pun. Lihat dokumentasi .The
Popen
kelas darisubprocess
modul. Ini dimaksudkan sebagai penggantios.popen
tetapi memiliki kelemahan karena sedikit lebih rumit karena begitu komprehensif. Misalnya, Anda akan mengatakan:dari pada:
tetapi menyenangkan untuk memiliki semua opsi di sana dalam satu kelas terpadu, bukan 4 fungsi popen berbeda. Lihat dokumentasi .
The
call
fungsi darisubprocess
modul. Ini pada dasarnya sepertiPopen
kelas dan mengambil semua argumen yang sama, tetapi hanya menunggu sampai perintah selesai dan memberi Anda kode kembali. Sebagai contoh:Lihat dokumentasi .
Jika Anda menggunakan Python 3.5 atau lebih baru, Anda dapat menggunakan
subprocess.run
fungsi baru , yang sangat mirip di atas tetapi bahkan lebih fleksibel dan mengembalikanCompletedProcess
objek ketika perintah selesai dijalankan.Modul os juga memiliki semua fungsi fork / exec / spawn yang Anda miliki dalam program C, tapi saya tidak merekomendasikan untuk menggunakannya secara langsung.
The
subprocess
Modul mungkin harus apa yang Anda gunakan.Akhirnya perlu diketahui bahwa untuk semua metode di mana Anda melewati perintah terakhir yang akan dieksekusi oleh shell sebagai string dan Anda bertanggung jawab untuk menghindarinya. Ada implikasi keamanan serius jika ada bagian dari string yang Anda lewati tidak dapat dipercaya sepenuhnya. Misalnya, jika pengguna memasukkan beberapa bagian string. Jika Anda tidak yakin, gunakan saja metode ini dengan konstanta. Untuk memberi Anda sedikit implikasi, pertimbangkan kode ini:
dan bayangkan bahwa pengguna memasukkan sesuatu "mama saya tidak mencintaiku &&rm -rf /" yang dapat menghapus seluruh sistem file.
sumber
open
.subprocess.run()
. docs.python.org/3.5/library/subprocess.html#subprocess.runsubprocess.run(..)
,, apa sebenarnya yang dilakukan "Ini tidak menangkap stdout atau stderr secara default." berarti? Bagaimana dengansubprocess.check_output(..)
dan STDERR?echo
depan string dilewatkanPopen
? Jadi perintah penuhnya adalahecho my mama didnt love me && rm -rf /
.subprocess.run()
saudara kandungnya sajasubprocess.check_call()
. Untuk kasus di mana ini tidak cukup, lihatsubprocess.Popen()
.os.popen()
mungkin seharusnya tidak disebutkan sama sekali, atau datang bahkan setelah "retas kode fork / exec / spawn Anda sendiri".Implementasi yang khas:
Anda bebas melakukan apa yang Anda inginkan dengan
stdout
data di dalam pipa. Bahkan, Anda bisa dengan mudah menghilangkan parameter tersebut (stdout=
danstderr=
) dan itu akan berperilaku sepertios.system()
.sumber
.readlines()
membaca semua baris sekaligus yaitu, itu memblokir sampai subproses keluar (menutup ujung pipa). Untuk membaca secara real time (jika tidak ada masalah buffering) Anda bisa:for line in iter(p.stdout.readline, ''): print line,
p.stdout.readline()
(catatan: tidak adas
di akhir) tidak akan melihat data apa pun sampai anak mengisi buffernya. Jika anak tidak menghasilkan banyak data maka hasilnya tidak akan secara real time. Lihat alasan kedua di T: Mengapa tidak menggunakan pipa (popen ())? . Beberapa solusi disediakan dalam jawaban ini (harap, pty, stdbuf)Popen
untuk tugas-tugas sederhana. Ini juga tidak perlu ditentukanshell=True
. Coba salah satusubprocess.run()
jawabannya.Beberapa petunjuk tentang melepaskan proses anak dari proses pemanggilan (memulai proses anak di latar belakang).
Misalkan Anda ingin memulai tugas panjang dari skrip CGI. Artinya, proses anak harus hidup lebih lama dari proses eksekusi skrip CGI.
Contoh klasik dari dokumentasi modul subproses adalah:
Idenya di sini adalah bahwa Anda tidak ingin menunggu di baris 'panggilan subproses' sampai longtask.py selesai. Tetapi tidak jelas apa yang terjadi setelah baris 'kode lagi di sini' dari contoh.
Platform target saya adalah FreeBSD, tetapi pengembangannya pada Windows, jadi saya menghadapi masalah pada Windows terlebih dahulu.
Di Windows (Windows XP), proses induk tidak akan selesai sampai longtask.py telah menyelesaikan pekerjaannya. Bukan apa yang Anda inginkan dalam skrip CGI. Masalahnya tidak spesifik untuk Python; dalam komunitas PHP masalahnya sama.
Solusinya adalah dengan melewati DETACHED_PROCESS Flag Pembuatan Proses ke fungsi CreateProcess yang mendasarinya di Windows API. Jika Anda telah menginstal pywin32, Anda dapat mengimpor flag dari modul win32process, jika tidak, Anda harus mendefinisikannya sendiri:
/ * UPD 2015.10.27 @eryksun dalam komentar di bawah ini mencatat, bahwa tanda yang benar secara semantik adalah CREATE_NEW_CONSOLE (0x00000010) * /
Pada FreeBSD kami memiliki masalah lain: ketika proses induk selesai, itu menyelesaikan proses anak juga. Dan itu juga bukan yang Anda inginkan dalam skrip CGI. Beberapa percobaan menunjukkan bahwa masalahnya sepertinya ada pada sharing sys.stdout. Dan solusi kerjanya adalah sebagai berikut:
Saya belum memeriksa kode di platform lain dan tidak tahu alasan perilaku di FreeBSD. Jika ada yang tahu, silakan bagikan ide Anda. Googling untuk memulai proses latar belakang dengan Python belum menjelaskan apa pun.
sumber
DETACHED_PROCESS
dalamcreationflags
menghindari ini dengan mencegah anak dari mewarisi atau membuat konsol. Jika Anda menginginkan konsol baru, gunakanCREATE_NEW_CONSOLE
(0x00000010).os.devnull
karena beberapa program konsol keluar dengan kesalahan sebaliknya. Buat konsol baru saat Anda ingin proses anak berinteraksi dengan pengguna secara bersamaan dengan proses induk. Akan membingungkan untuk mencoba melakukan keduanya dalam satu jendela.Perhatikan bahwa ini berbahaya, karena perintahnya tidak dibersihkan. Saya serahkan kepada Anda untuk google untuk dokumentasi yang relevan pada modul 'os' dan 'sys'. Ada banyak fungsi (exec * dan spawn *) yang akan melakukan hal serupa.
sumber
subprocess
sebagai solusi yang sedikit lebih fleksibel dan portabel. Menjalankan perintah eksternal tentu saja secara inheren tidak dapat diangkut (Anda harus memastikan perintah itu tersedia pada setiap arsitektur yang Anda butuhkan untuk mendukung) dan meneruskan input pengguna sebagai perintah eksternal secara inheren tidak aman.Saya akan merekomendasikan menggunakan modul subproses daripada os.system karena shell melarikan diri untuk Anda dan karenanya jauh lebih aman.
sumber
subprocess
kapanshell=False
, maka gunakanshlex.split
cara mudah untuk melakukan ini docs.python.org/2/library/shlex.html#shlex.split ( itu cara yang disarankan menurut docs docs.python.org/2/library/subprocess.html#popen-constructor )Jika Anda ingin mengembalikan hasil perintah, Anda dapat menggunakan
os.popen
. Namun, ini sudah tidak berlaku lagi sejak versi 2.6 mendukung modul subproses , yang telah dijawab dengan baik oleh jawaban lain.sumber
Ada banyak pustaka yang berbeda yang memungkinkan Anda untuk memanggil perintah eksternal dengan Python. Untuk setiap perpustakaan saya telah memberikan deskripsi dan menunjukkan contoh memanggil perintah eksternal. Perintah yang saya gunakan sebagai contoh adalah
ls -l
(daftar semua file). Jika Anda ingin mengetahui lebih lanjut tentang perpustakaan yang saya daftarkan dan tautkan dokumentasi untuk masing-masing perpustakaan.Semoga ini akan membantu Anda membuat keputusan tentang perpustakaan mana yang akan digunakan :)
Subprocess memungkinkan Anda untuk memanggil perintah eksternal dan menghubungkannya ke input / output / pipa kesalahan mereka (stdin, stdout, dan stderr). Subprocess adalah pilihan default untuk menjalankan perintah, tetapi terkadang modul lain lebih baik.
os digunakan untuk "fungsionalitas tergantung sistem operasi". Itu juga dapat digunakan untuk memanggil perintah eksternal dengan
os.system
danos.popen
(Catatan: Ada juga subprocess.popen). os akan selalu menjalankan shell dan merupakan alternatif sederhana untuk orang yang tidak perlu, atau tidak tahu cara menggunakannyasubprocess.run
.sh adalah antarmuka subproses yang memungkinkan Anda memanggil program seolah-olah itu adalah fungsi. Ini berguna jika Anda ingin menjalankan perintah beberapa kali.
Plumbum adalah pustaka untuk program Python "mirip script". Anda dapat memanggil program seperti fungsi seperti pada
sh
. Timah hitam berguna jika Anda ingin menjalankan pipa tanpa cangkang.pexpect memungkinkan Anda menelurkan aplikasi anak, mengontrolnya dan menemukan pola dalam hasilnya. Ini adalah alternatif yang lebih baik daripada subproses untuk perintah yang mengharapkan tty di Unix.
fabric adalah pustaka Python 2.5 dan 2.7. Ini memungkinkan Anda untuk menjalankan perintah shell lokal dan jarak jauh. Fabric adalah alternatif sederhana untuk menjalankan perintah dalam secure shell (SSH)
Utusan itu dikenal sebagai "subproses untuk manusia". Ini digunakan sebagai pembungkus kenyamanan di sekitar
subprocess
modul.commands
berisi fungsi wrapper untukos.popen
, tetapi telah dihapus dari Python 3 karenasubprocess
merupakan alternatif yang lebih baik.Hasil edit didasarkan pada komentar JF Sebastian.
sumber
Saya selalu menggunakan
fabric
untuk hal-hal ini seperti:Tapi ini tampaknya menjadi alat yang bagus:
sh
(Python antarmuka proses) .Lihatlah sebuah contoh:
sumber
Periksa perpustakaan Python "pexpect" juga.
Hal ini memungkinkan untuk mengendalikan interaktif program / perintah eksternal, bahkan ssh, ftp, telnet, dll. Anda dapat mengetik sesuatu seperti:
sumber
Dengan perpustakaan standar
Gunakan modul subproses (Python 3):
Ini adalah cara standar yang disarankan. Namun, tugas yang lebih rumit (pipa, output, input, dll.) Dapat membosankan untuk dibangun dan ditulis.
Catatan tentang versi Python: Jika Anda masih menggunakan Python 2, subprocess.call bekerja dengan cara yang sama.
ProTip: shlex.split dapat membantu Anda mem-parsing perintah untuk
run
,,call
dansubprocess
fungsi - fungsi lain jika Anda tidak ingin (atau tidak bisa!) Menyediakannya dalam bentuk daftar:Dengan dependensi eksternal
Jika Anda tidak keberatan dengan ketergantungan eksternal, gunakan timah hitam :
Ini adalah
subprocess
pembungkus terbaik . Ini cross-platform, yaitu bekerja pada sistem Windows dan Unix-like. Instal olehpip install plumbum
.Perpustakaan populer lainnya adalah sh :
Namun,
sh
menjatuhkan dukungan Windows, jadi itu tidak sehebat dulu. Instal olehpip install sh
.sumber
Jika Anda membutuhkan output dari perintah yang Anda panggil, maka Anda dapat menggunakan subprocess.check_output (Python 2.7+).
Perhatikan juga parameter shell .
sumber
check_output
membutuhkan daftar daripada string. Jika Anda tidak mengandalkan spasi yang dikutip untuk menjadikan panggilan Anda valid, cara paling sederhana dan paling mudah dibaca untuk melakukannya adalahsubprocess.check_output("ls -l /dev/null".split())
.Ini adalah bagaimana saya menjalankan perintah saya. Kode ini memiliki semua yang Anda butuhkan
sumber
Memperbarui:
subprocess.run
adalah pendekatan yang disarankan pada Python 3.5 jika kode Anda tidak perlu mempertahankan kompatibilitas dengan versi Python sebelumnya. Ini lebih konsisten dan menawarkan kemudahan penggunaan yang sama seperti Utusan. (Pipa tidak sejelas mungkin. Lihat pertanyaan ini untuk caranya .)Berikut beberapa contoh dari dokumentasi .
Jalankan proses:
Naikkan saat gagal dijalankan:
Capture output:
Jawaban asli:
Saya sarankan mencoba Utusan . Ini adalah pembungkus untuk subproses, yang pada gilirannya bertujuan untuk menggantikan modul dan fungsi yang lebih lama. Utusan adalah proses bagi manusia.
Contoh penggunaan dari README :
Barang pipa sekitar juga:
sumber
Gunakan subproses .
... atau untuk perintah yang sangat sederhana:
sumber
os.system
OK, tapi agak tanggal. Ini juga tidak terlalu aman. Sebagai gantinya, cobalahsubprocess
.subprocess
tidak memanggil sh secara langsung dan karenanya lebih aman daripadaos.system
.Dapatkan informasi lebih lanjut di sini .
sumber
subprocess
tidak menghapus semua masalah keamanan, dan memiliki beberapa masalah sial sendiri.Sederhana, gunakan
subprocess.run
, yang mengembalikanCompletedProcess
objek:Mengapa?
Pada Python 3.5, dokumentasi merekomendasikan subprocess.run :
Berikut adalah contoh penggunaan paling sederhana yang mungkin - dan tidak persis seperti yang diminta:
run
menunggu perintah berhasil diselesaikan, lalu mengembalikanCompletedProcess
objek. Alih-alih itu dapat meningkatkanTimeoutExpired
(jika Anda memberikantimeout=
argumen) atauCalledProcessError
(jika gagal dan Anda luluscheck=True
).Seperti yang Anda simpulkan dari contoh di atas, stdout dan stderr keduanya disalurkan ke stdout dan stderr Anda secara default.
Kita dapat memeriksa objek yang dikembalikan dan melihat perintah yang diberikan dan kode pengembalian:
Menangkap output
Jika Anda ingin menangkap output, Anda dapat meneruskan
subprocess.PIPE
ke yang sesuaistderr
ataustdout
:(Saya menemukan itu menarik dan sedikit berlawanan dengan intuisi bahwa info versi dimasukkan ke stderr bukan stdout.)
Lulus daftar perintah
Seseorang mungkin dengan mudah berpindah dari menyediakan string perintah secara manual (seperti yang disarankan pertanyaan) ke menyediakan string yang dibuat secara terprogram. Jangan membangun string secara pemrograman. Ini adalah masalah keamanan potensial. Lebih baik untuk menganggap Anda tidak percaya input.
Catatan, hanya
args
harus dilewati secara posisi.Tanda tangan penuh
Inilah tanda tangan aktual di sumber dan seperti yang ditunjukkan oleh
help(run)
:The
popenargs
dankwargs
diberikan kepadaPopen
konstruktor.input
dapat berupa serangkaian byte (atau unicode, jika ditentukan penyandian atauuniversal_newlines=True
) yang akan disalurkan ke stdin subproses.Dokumentasi menjelaskan
timeout=
dancheck=True
lebih baik daripada yang saya bisa:dan contoh ini untuk
check=True
lebih baik daripada yang saya dapat dengan:Tanda Tangan yang Diperluas
Berikut tanda tangan yang diperluas, seperti yang diberikan dalam dokumentasi:
Perhatikan bahwa ini menunjukkan bahwa hanya daftar args yang harus dilewati secara posisi. Jadi berikan argumen yang tersisa sebagai argumen kata kunci.
Popen
Kapan digunakan
Popen
sebagai gantinya? Saya akan berjuang untuk menemukan use case berdasarkan argumen saja.Popen
Namun, penggunaan langsung akan memberi Anda akses ke metode-metodenya, termasukpoll
, 'send_signal', 'terminate', dan 'wait'.Inilah
Popen
tanda tangan seperti yang diberikan dalam sumber . Saya pikir ini adalah enkapsulasi informasi yang paling tepat (sebagai lawan darihelp(Popen)
):Tapi yang lebih informatif adalah yang
Popen
dokumentasi :Memahami dokumentasi yang tersisa
Popen
akan dibiarkan sebagai latihan bagi pembaca.sumber
shell=True
atau (lebih baik lagi) melewati perintah sebagai daftar.Ada juga Timah
sumber
Menggunakan:
os - Modul ini menyediakan cara portabel menggunakan fungsionalitas yang tergantung pada sistem operasi.
Untuk lebih banyak
os
fungsi, berikut adalah dokumentasi.sumber
Ini bisa sesederhana ini:
sumber
os.system
secara eksplisit merekomendasikan untuk tidak mendukungnyasubprocess
.Saya cukup suka shell_command karena kesederhanaannya. Itu dibangun di atas modul subproses.
Berikut ini contoh dari dokumentasi:
sumber
Ada perbedaan lain di sini yang tidak disebutkan sebelumnya.
subprocess.Popen
mengeksekusi <perintah> sebagai subproses. Dalam kasus saya, saya perlu menjalankan file <a> yang perlu berkomunikasi dengan program lain, <b>.Saya mencoba subproses, dan eksekusi berhasil. Namun <b> tidak dapat berkomunikasi dengan <a>. Semuanya normal ketika saya menjalankan keduanya dari terminal.
Satu lagi: (CATATAN: kwrite berperilaku berbeda dari aplikasi lain. Jika Anda mencoba yang di bawah ini dengan Firefox, hasilnya tidak akan sama.)
Jika Anda mencoba
os.system("kwrite")
, aliran program membeku hingga pengguna menutup kwrite. Untuk mengatasinya saya malah mencobaos.system(konsole -e kwrite)
. Program waktu ini terus mengalir, tetapi kwrite menjadi subproses konsol.Siapa pun yang menjalankan kwrite bukan menjadi subproses (yaitu di monitor sistem, ia harus muncul di tepi paling kiri dari pohon).
sumber
os.system
tidak memungkinkan Anda untuk menyimpan hasil, jadi jika Anda ingin menyimpan hasil dalam beberapa daftar atau sesuatu, sebuahsubprocess.call
karya.sumber
subprocess.check_call
nyaman jika Anda tidak ingin menguji nilai pengembalian. Itu melempar pengecualian pada kesalahan apa pun.sumber
Saya cenderung menggunakan subproses bersama dengan shlex (untuk menangani pelolosan string yang dikutip):
sumber
Shameless plug, saya menulis sebuah perpustakaan untuk ini: P https://github.com/houqp/shell.py
Ini pada dasarnya pembungkus popen dan shlex untuk saat ini. Ini juga mendukung perintah perpipaan sehingga Anda dapat melakukan perintah dengan lebih mudah dengan Python. Jadi Anda dapat melakukan hal-hal seperti:
sumber
Di Windows Anda hanya dapat mengimpor
subprocess
modul dan menjalankan perintah eksternal dengan memanggilsubprocess.Popen()
,subprocess.Popen().communicate()
dansubprocess.Popen().wait()
seperti di bawah ini:Keluaran:
sumber
Di Linux, jika Anda ingin memanggil perintah eksternal yang akan dieksekusi secara independen (akan tetap berjalan setelah skrip python berakhir), Anda dapat menggunakan antrian sederhana sebagai spooler tugas atau perintah at
Contoh dengan pengumpul tugas:
Catatan tentang spooler tugas (
ts
):Anda bisa mengatur jumlah proses bersamaan untuk dijalankan ("slot") dengan:
ts -S <number-of-slots>
Instalasi
ts
tidak memerlukan hak admin. Anda dapat mengunduh dan mengompilasinya dari sumber dengan sederhanamake
, menambahkannya ke jalur Anda dan selesai.sumber
ts
bukan standar pada setiap distro yang saya tahu, meskipun pointer keat
agak berguna. Anda mungkin juga harus menyebutkanbatch
. Seperti di tempat lain,os.system()
rekomendasi mungkin setidaknya harus menyebutkan bahwa itusubprocess
adalah penggantian yang direkomendasikan.Anda dapat menggunakan Popen, dan kemudian Anda dapat memeriksa status prosedur:
Lihat subproses.Popen .
sumber
Untuk mengambil id jaringan dari OpenStack Neutron :
Output dari daftar nova
Output cetak (networkId)
sumber
os.popen()
pada 2016. Skrip Awk dapat dengan mudah diganti dengan kode Python asli.