Memanggil perintah eksternal dari Python

4886

Bagaimana Anda memanggil perintah eksternal (seolah-olah saya mengetiknya di shell Unix atau command prompt Windows) dari dalam skrip Python?

freshWoWer
sumber

Jawaban:

4696

Lihatlah modul subproses di perpustakaan standar:

import subprocess
subprocess.run(["ls", "-l"])

Keuntungan dari subprocessvs systemadalah bahwa hal itu lebih fleksibel (Anda bisa mendapatkan stdout, stderr, "nyata" kode status, penanganan kesalahan yang lebih baik, dll ...).

The dokumentasi resmi merekomendasikan subprocessmodul atas alternatif os.system():

The subprocessModul 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 subprocessdokumentasi mungkin memiliki beberapa resep yang bermanfaat.

Untuk versi Python sebelum 3.5, gunakan call:

import subprocess
subprocess.call(["ls", "-l"])
David Cournapeau
sumber
Apakah ada cara untuk menggunakan substitusi variabel? IE yang saya coba lakukan echo $PATHdengan menggunakan call(["echo", "$PATH"]), tetapi itu hanya menggemakan string literal $PATHdaripada 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.
Kevin Wheeler 15
@KevinWheeler Anda harus menggunakannya shell=Trueagar bisa berfungsi.
SethMMorton
39
@KevinWheeler Anda TIDAK boleh menggunakan 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 = True
Murmel
apakah panggilan blok? yaitu jika saya ingin menjalankan beberapa perintah dalam satu forlingkaran, bagaimana cara melakukannya tanpa memblokir skrip python saya? Saya tidak peduli dengan output dari perintah saya hanya ingin menjalankan banyak dari mereka.
Charlie Parker
5
Jika Anda ingin membuat daftar dari perintah dengan parameter , daftar yang dapat digunakan subprocesskapan shell=False, maka gunakan shlex.splitcara mudah untuk melakukan ini docs.python.org/2/library/shlex.html#shlex.split
Daniel F
2985

Berikut ini ringkasan cara memanggil program eksternal serta kelebihan dan kekurangan masing-masing:

  1. 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:

    os.system("some_command < input_file | another_command > output_file")  

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 .

  1. stream = os.popen("some_command with args")akan melakukan hal yang sama seperti os.systemkecuali 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 .

  2. The Popenkelas dari subprocessmodul. Ini dimaksudkan sebagai pengganti os.popentetapi memiliki kelemahan karena sedikit lebih rumit karena begitu komprehensif. Misalnya, Anda akan mengatakan:

    print subprocess.Popen("echo Hello World", shell=True, stdout=subprocess.PIPE).stdout.read()

    dari pada:

    print os.popen("echo Hello World").read()

    tetapi menyenangkan untuk memiliki semua opsi di sana dalam satu kelas terpadu, bukan 4 fungsi popen berbeda. Lihat dokumentasi .

  3. The callfungsi dari subprocessmodul. Ini pada dasarnya seperti Popenkelas dan mengambil semua argumen yang sama, tetapi hanya menunggu sampai perintah selesai dan memberi Anda kode kembali. Sebagai contoh:

    return_code = subprocess.call("echo Hello World", shell=True)  

    Lihat dokumentasi .

  4. Jika Anda menggunakan Python 3.5 atau lebih baru, Anda dapat menggunakan subprocess.runfungsi baru , yang sangat mirip di atas tetapi bahkan lebih fleksibel dan mengembalikan CompletedProcessobjek ketika perintah selesai dijalankan.

  5. Modul os juga memiliki semua fungsi fork / exec / spawn yang Anda miliki dalam program C, tapi saya tidak merekomendasikan untuk menggunakannya secara langsung.

The subprocessModul 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:

print subprocess.Popen("echo %s " % user_input, stdout=PIPE).stdout.read()

dan bayangkan bahwa pengguna memasukkan sesuatu "mama saya tidak mencintaiku &&rm -rf /" yang dapat menghapus seluruh sistem file.

Eli Courtwright
sumber
22
Jawaban / penjelasan yang bagus. Bagaimana jawaban ini membenarkan moto Python seperti yang dijelaskan dalam artikel ini? fastcompany.com/3026446/... "Secara gaya, Perl dan Python memiliki filosofi yang berbeda. Moto terkenal Perl adalah" Ada Lebih Dari Satu Cara untuk Melakukannya ". Python dirancang untuk memiliki satu cara yang jelas untuk melakukannya" Sepertinya itu seharusnya Jalan lain! Di Perl saya hanya tahu dua cara untuk mengeksekusi perintah - menggunakan back-tick atau open.
Jean
12
Jika menggunakan Python 3.5+, gunakan subprocess.run(). docs.python.org/3.5/library/subprocess.html#subprocess.run
phoenix
4
Yang biasanya perlu diketahui adalah apa yang dilakukan dengan STDOUT dan STDERR proses anak, karena jika mereka diabaikan, dalam beberapa kondisi (sangat umum), pada akhirnya proses anak akan mengeluarkan panggilan sistem untuk menulis ke STDOUT (STDERR juga?) yang akan melebihi buffer output yang disediakan untuk proses oleh OS, dan OS akan membuatnya memblokir sampai beberapa proses membaca dari buffer itu. Jadi, dengan cara yang direkomendasikan saat ini subprocess.run(..),, apa sebenarnya yang dilakukan "Ini tidak menangkap stdout atau stderr secara default." berarti? Bagaimana dengan subprocess.check_output(..)dan STDERR?
Evgeni Sergeev
2
@ Pitto ya, tapi bukan itu yang dijalankan oleh contoh. Perhatikan bagian echodepan string dilewatkan Popen? Jadi perintah penuhnya adalah echo my mama didnt love me && rm -rf /.
Chris Arndt
6
Ini bisa dibilang jalan yang salah. Sebagian besar orang hanya membutuhkan subprocess.run()saudara kandungnya saja subprocess.check_call(). Untuk kasus di mana ini tidak cukup, lihat subprocess.Popen(). os.popen()mungkin seharusnya tidak disebutkan sama sekali, atau datang bahkan setelah "retas kode fork / exec / spawn Anda sendiri".
tripleee
358

Implementasi yang khas:

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()

Anda bebas melakukan apa yang Anda inginkan dengan stdoutdata di dalam pipa. Bahkan, Anda bisa dengan mudah menghilangkan parameter tersebut ( stdout=dan stderr=) dan itu akan berperilaku seperti os.system().

EmmEff
sumber
44
.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,
jfs
1
Bisakah Anda menguraikan apa yang Anda maksud dengan "jika tidak ada masalah buffering"? Jika proses memblokir pasti, panggilan subproses juga memblokir. Hal yang sama dapat terjadi dengan contoh asli saya juga. Apa lagi yang bisa terjadi sehubungan dengan buffering?
EmmEff
15
proses anak dapat menggunakan buffer-blok dalam mode non-interaktif alih-alih buffer-line jadi p.stdout.readline()(catatan: tidak ada sdi 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)
jfs
4
masalah buffering hanya masalah jika Anda ingin output secara real time dan tidak berlaku untuk kode Anda yang tidak mencetak apa pun sampai semua data diterima
jfs
3
Jawaban ini baik untuk waktunya, tetapi kami tidak lagi merekomendasikan Popenuntuk tugas-tugas sederhana. Ini juga tidak perlu ditentukan shell=True. Coba salah satu subprocess.run()jawabannya.
tripleee
230

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:

import subprocess
import sys

# Some code here

pid = subprocess.Popen([sys.executable, "longtask.py"]) # Call subprocess

# Some more code here

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:

DETACHED_PROCESS = 0x00000008

pid = subprocess.Popen([sys.executable, "longtask.py"],
                       creationflags=DETACHED_PROCESS).pid

/ * 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:

pid = subprocess.Popen([sys.executable, "longtask.py"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)

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.

baru ditemukan
sumber
saya perhatikan kemungkinan "quirk" dengan mengembangkan aplikasi py2exe di pydev + eclipse. saya dapat mengatakan bahwa skrip utama tidak terlepas karena jendela output eclipse tidak berakhir; bahkan jika skrip dieksekusi sampai selesai masih menunggu untuk dikembalikan. tetapi, ketika saya mencoba mengkompilasi ke executable py2exe, perilaku yang diharapkan terjadi (menjalankan proses sebagai terpisah, lalu berhenti). saya tidak yakin, tetapi nama yang dapat dieksekusi tidak ada dalam daftar proses lagi. ini bekerja untuk semua pendekatan (os.system ("start *"), os.spawnl dengan os.P_DETACH, subprocs, dll.
maranas
1
Anda mungkin juga perlu bendera CREATE_NEW_PROCESS_GROUP. Lihat Popen menunggu proses anak bahkan ketika anak terdekatnya telah dihentikan
jfs
5
Berikut ini salah: "[o] n windows (win xp), proses induk tidak akan selesai sampai longtask.py telah menyelesaikan pekerjaannya". Orang tua akan keluar secara normal, tetapi jendela konsol (contoh conhost.exe) hanya ditutup ketika proses yang terlampir terakhir keluar, dan anak tersebut mungkin telah mewarisi konsol induk. Pengaturan DETACHED_PROCESSdalam creationflagsmenghindari ini dengan mencegah anak dari mewarisi atau membuat konsol. Jika Anda menginginkan konsol baru, gunakan CREATE_NEW_CONSOLE(0x00000010).
Eryk Sun
1
Saya tidak bermaksud bahwa mengeksekusi sebagai proses terpisah tidak benar. Yang mengatakan, Anda mungkin perlu mengatur pegangan standar ke file, pipa, atau os.devnullkarena 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.
Eryk Sun
1
apakah tidak ada cara OS-agnostik untuk menjalankan proses di latar belakang?
Charlie Parker
152
import os
os.system("your command")

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.

nimish
sumber
6
Tidak tahu apa yang saya maksudkan hampir satu dekade yang lalu (periksa tanggalnya!), Tetapi jika saya harus menebak, itu berarti tidak ada validasi yang dilakukan.
nimish
1
Ini sekarang seharusnya menunjuk subprocesssebagai 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.
tripleee
1
Perhatikan cap waktu pada orang ini: jawaban "benar" memiliki 40x suara dan jawaban # 1.
nimish
Satu-satunya solusi yang berhasil bagi saya untuk menjalankan hal-hal NodeJS.
Nikolay Shindarov
148

Saya akan merekomendasikan menggunakan modul subproses daripada os.system karena shell melarikan diri untuk Anda dan karenanya jauh lebih aman.

subprocess.call(['ping', 'localhost'])
sirwart
sumber
Jika Anda ingin membuat daftar dari perintah dengan parameter , daftar yang dapat digunakan subprocesskapan shell=False, maka gunakan shlex.splitcara 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 )
Daniel F
6
Ini tidak benar: " itu shell melarikan diri untuk Anda dan karena itu jauh lebih aman ". subproses tidak melakukan shell escape, subprocess tidak meneruskan perintah Anda melalui shell, jadi tidak perlu shell melarikan diri.
Lie Ryan
144
import os
cmd = 'ls -al'
os.system(cmd)

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.

Alexandra Franks
sumber
10
popen tidak digunakan lagi untuk subproses .
Fox Wilson
Anda juga dapat menyimpan hasil Anda dengan panggilan sistem os.s, karena berfungsi seperti shell UNIX itu sendiri, seperti misalnya sistem os.system ('ls -l> test2.txt')
Stefan Gruenwald
97

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.

Sumber:

Ini semua perpustakaan:

Semoga ini akan membantu Anda membuat keputusan tentang perpustakaan mana yang akan digunakan :)

subproses

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.

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

os digunakan untuk "fungsionalitas tergantung sistem operasi". Itu juga dapat digunakan untuk memanggil perintah eksternal dengan os.systemdan os.popen(Catatan: Ada juga subprocess.popen). os akan selalu menjalankan shell dan merupakan alternatif sederhana untuk orang yang tidak perlu, atau tidak tahu cara menggunakannya subprocess.run.

os.system("ls -l") # run command
os.popen("ls -l").read() # This will run the command and return any output

SH

sh adalah antarmuka subproses yang memungkinkan Anda memanggil program seolah-olah itu adalah fungsi. Ini berguna jika Anda ingin menjalankan perintah beberapa kali.

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

timah hitam

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.

ls_cmd = plumbum.local("ls -l") # get command
ls_cmd() # run command

harap

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.

pexpect.run("ls -l") # Run command as normal
child = pexpect.spawn('scp foo [email protected]:.') # Spawns child application
child.expect('Password:') # When this is the output
child.sendline('mypassword')

kain

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)

fabric.operations.local('ls -l') # Run command as normal
fabric.operations.local('ls -l', capture = True) # Run command and receive output

utusan

Utusan itu dikenal sebagai "subproses untuk manusia". Ini digunakan sebagai pembungkus kenyamanan di sekitar subprocessmodul.

r = envoy.run("ls -l") # Run command
r.std_out # get output

perintah

commandsberisi fungsi wrapper untuk os.popen, tetapi telah dihapus dari Python 3 karena subprocessmerupakan alternatif yang lebih baik.

Hasil edit didasarkan pada komentar JF Sebastian.

Tom Fuller
sumber
74

Saya selalu menggunakan fabricuntuk hal-hal ini seperti:

from fabric.operations import local
result = local('ls', capture=True)
print "Content:/n%s" % (result, )

Tapi ini tampaknya menjadi alat yang bagus: sh(Python antarmuka proses) .

Lihatlah sebuah contoh:

from sh import vgdisplay
print vgdisplay()
print vgdisplay('-v')
print vgdisplay(v=True)
Jorge E. Cardona
sumber
73

Periksa perpustakaan Python "pexpect" juga.

Hal ini memungkinkan untuk mengendalikan interaktif program / perintah eksternal, bahkan ssh, ftp, telnet, dll. Anda dapat mengetik sesuatu seperti:

child = pexpect.spawn('ftp 192.168.0.24')

child.expect('(?i)name .*: ')

child.sendline('anonymous')

child.expect('(?i)password')
athanassis
sumber
70

Dengan perpustakaan standar

Gunakan modul subproses (Python 3):

import subprocess
subprocess.run(['ls', '-l'])

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,, calldan subprocessfungsi - fungsi lain jika Anda tidak ingin (atau tidak bisa!) Menyediakannya dalam bentuk daftar:

import shlex
import subprocess
subprocess.run(shlex.split('ls -l'))

Dengan dependensi eksternal

Jika Anda tidak keberatan dengan ketergantungan eksternal, gunakan timah hitam :

from plumbum.cmd import ifconfig
print(ifconfig['wlan0']())

Ini adalah subprocesspembungkus terbaik . Ini cross-platform, yaitu bekerja pada sistem Windows dan Unix-like. Instal olehpip install plumbum .

Perpustakaan populer lainnya adalah sh :

from sh import ifconfig
print(ifconfig('wlan0'))

Namun, shmenjatuhkan dukungan Windows, jadi itu tidak sehebat dulu. Instal oleh pip install sh.

Honza Javorek
sumber
69

Jika Anda membutuhkan output dari perintah yang Anda panggil, maka Anda dapat menggunakan 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'

Perhatikan juga parameter shell .

Jika shell True, perintah yang ditentukan akan dieksekusi melalui shell. Ini dapat berguna jika Anda menggunakan Python terutama untuk meningkatkan aliran kontrol yang ditawarkannya pada sebagian besar sistem shell dan masih menginginkan akses yang mudah ke fitur shell lainnya seperti pipa shell, wildcard nama file, ekspansi variabel lingkungan, dan perluasan ~ ke rumah pengguna direktori. Namun, catatan bahwa Python sendiri menawarkan implementasi dari banyak shell-seperti fitur (khususnya, glob, fnmatch, os.walk(), os.path.expandvars(), os.path.expanduser(), dan shutil).

Facundo Casco
sumber
1
Catatan yang check_outputmembutuhkan daftar daripada string. Jika Anda tidak mengandalkan spasi yang dikutip untuk menjadikan panggilan Anda valid, cara paling sederhana dan paling mudah dibaca untuk melakukannya adalah subprocess.check_output("ls -l /dev/null".split()).
Bruno Bronosky
56

Ini adalah bagaimana saya menjalankan perintah saya. Kode ini memiliki semua yang Anda butuhkan

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()
Usman Khan
sumber
3
Saya pikir itu dapat diterima untuk perintah yang dikodekan, jika itu meningkatkan keterbacaan.
Adam Matan
54

Memperbarui:

subprocess.runadalah 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:

>>> subprocess.run(["ls", "-l"])  # Doesn't capture output
CompletedProcess(args=['ls', '-l'], returncode=0)

Naikkan saat gagal dijalankan:

>>> subprocess.run("exit 1", shell=True, check=True)
Traceback (most recent call last):
  ...
subprocess.CalledProcessError: Command 'exit 1' returned non-zero exit status 1

Capture output:

>>> 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')

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 :

>>> 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
''

Barang pipa sekitar juga:

>>> r = envoy.run('uptime | pbcopy')

>>> r.command
'pbcopy'
>>> r.status_code
0

>>> r.history
[<Response 'uptime'>]
Joe
sumber
43

Gunakan subproses .

... atau untuk perintah yang sangat sederhana:

import os
os.system('cat testfile')
Ben Hoffstein
sumber
36

os.systemOK, tapi agak tanggal. Ini juga tidak terlalu aman. Sebagai gantinya, cobalah subprocess. subprocesstidak memanggil sh secara langsung dan karenanya lebih aman daripada os.system.

Dapatkan informasi lebih lanjut di sini .

Martin W
sumber
2
Sementara saya setuju dengan rekomendasi keseluruhan, subprocesstidak menghapus semua masalah keamanan, dan memiliki beberapa masalah sial sendiri.
tripleee
36

Memanggil perintah eksternal dengan Python

Sederhana, gunakan subprocess.run, yang mengembalikan CompletedProcessobjek:

>>> 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)

Mengapa?

Pada Python 3.5, dokumentasi merekomendasikan subprocess.run :

Pendekatan yang disarankan untuk menjalankan subproses adalah menggunakan fungsi run () untuk semua kasus penggunaan yang dapat ditangani. Untuk kasus penggunaan yang lebih lanjut, antarmuka Popen yang mendasarinya dapat digunakan secara langsung.

Berikut adalah contoh penggunaan paling sederhana yang mungkin - dan tidak persis seperti yang diminta:

>>> 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)

runmenunggu perintah berhasil diselesaikan, lalu mengembalikan CompletedProcessobjek. Alih-alih itu dapat meningkatkan TimeoutExpired(jika Anda memberikan timeout=argumen) atau CalledProcessError(jika gagal dan Anda lulus check=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:

>>> completed_process.args
'python --version'
>>> completed_process.returncode
0

Menangkap output

Jika Anda ingin menangkap output, Anda dapat meneruskan subprocess.PIPEke yang sesuai stderratau 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''

(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.

>>> 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'

Catatan, hanya argsharus dilewati secara posisi.

Tanda tangan penuh

Inilah tanda tangan aktual di sumber dan seperti yang ditunjukkan oleh help(run):

def run(*popenargs, input=None, timeout=None, check=False, **kwargs):

The popenargsdan kwargsdiberikan kepada Popenkonstruktor. inputdapat berupa serangkaian byte (atau unicode, jika ditentukan penyandian atau universal_newlines=True) yang akan disalurkan ke stdin subproses.

Dokumentasi menjelaskan timeout=dan check=Truelebih baik daripada yang saya bisa:

Argumen batas waktu diteruskan ke Popen.communicate (). Jika batas waktu berakhir, proses anak akan dibunuh dan menunggu. Pengecualian TimeoutExpired akan dimunculkan kembali setelah proses anak dihentikan.

Jika check benar, dan proses keluar dengan kode keluar non-nol, pengecualian CalledProcessError akan dimunculkan. Atribut pengecualian itu menyimpan argumen, kode keluar, dan stdout dan stderr jika ditangkap.

dan contoh ini untuk check=Truelebih baik daripada yang saya dapat dengan:

>>> subprocess.run("exit 1", shell=True, check=True)
Traceback (most recent call last):
  ...
subprocess.CalledProcessError: Command 'exit 1' returned non-zero exit status 1

Tanda Tangan yang Diperluas

Berikut tanda tangan yang diperluas, seperti yang diberikan dalam dokumentasi:

subprocess.run(args, *, stdin=None, input=None, stdout=None, stderr=None, 
shell=False, cwd=None, timeout=None, check=False, encoding=None, 
errors=None)

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 Popensebagai gantinya? Saya akan berjuang untuk menemukan use case berdasarkan argumen saja. PopenNamun, penggunaan langsung akan memberi Anda akses ke metode-metodenya, termasuk poll, 'send_signal', 'terminate', dan 'wait'.

Inilah Popentanda tangan seperti yang diberikan dalam sumber . Saya pikir ini adalah enkapsulasi informasi yang paling tepat (sebagai lawan dari 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):

Tapi yang lebih informatif adalah yang Popendokumentasi :

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)

Jalankan program anak dalam proses baru. Pada POSIX, kelas menggunakan perilaku seperti os.execvp () - untuk menjalankan program anak. Pada Windows, kelas menggunakan fungsi Windows CreateProcess (). Argumen untuk Popen adalah sebagai berikut.

Memahami dokumentasi yang tersisa Popenakan dibiarkan sebagai latihan bagi pembaca.

Aaron Hall
sumber
Contoh sederhana komunikasi dua arah antara proses utama dan subproses dapat ditemukan di sini: stackoverflow.com/a/52841475/1349673
James Hirschorn
Contoh pertama mungkin harus memiliki shell=Trueatau (lebih baik lagi) melewati perintah sebagai daftar.
tripleee
33

Ada juga Timah

>>> 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
macet
sumber
28

Menggunakan:

import os

cmd = 'ls -al'

os.system(cmd)

os - Modul ini menyediakan cara portabel menggunakan fungsionalitas yang tergantung pada sistem operasi.

Untuk lebih banyak osfungsi, berikut adalah dokumentasi.

Priyankara
sumber
2
itu juga sudah usang. gunakan subproses
Corey Goldberg
28

Ini bisa sesederhana ini:

import os
cmd = "your command"
os.system(cmd)
Samadi Salahedine
sumber
1
Ini gagal menunjukkan kelemahannya, yang dijelaskan lebih detail dalam PEP-324 . Dokumentasi untuk os.systemsecara eksplisit merekomendasikan untuk tidak mendukungnya subprocess.
tripleee
25

Saya cukup suka shell_command karena kesederhanaannya. Itu dibangun di atas modul subproses.

Berikut ini contoh dari dokumentasi:

>>> 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
mdwhatcott
sumber
24

Ada perbedaan lain di sini yang tidak disebutkan sebelumnya.

subprocess.Popenmengeksekusi <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 mencoba os.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).

Atinc Delican
sumber
1
Apa yang Anda maksud dengan "Siapa saja yang menjalankan kwrite bukan menjadi subproses" ?
Peter Mortensen
23

os.systemtidak memungkinkan Anda untuk menyimpan hasil, jadi jika Anda ingin menyimpan hasil dalam beberapa daftar atau sesuatu, sebuah subprocess.callkarya.

Saurabh Bangad
sumber
22

subprocess.check_callnyaman jika Anda tidak ingin menguji nilai pengembalian. Itu melempar pengecualian pada kesalahan apa pun.

cdunn2001
sumber
22

Saya cenderung menggunakan subproses bersama dengan shlex (untuk menangani pelolosan string yang dikutip):

>>> 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)
Emil Stenström
sumber
17

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:

ex('echo hello shell.py') | "awk '{print $2}'"
houqp
sumber
16

Di Windows Anda hanya dapat mengimpor subprocessmodul dan menjalankan perintah eksternal dengan memanggil subprocess.Popen(), subprocess.Popen().communicate()dan subprocess.Popen().wait()seperti di bawah ini:

# 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)

Keluaran:

This process detail:
python.exe                     604 RDP-Tcp#0                  4      5,660 K
Swadhikar C
sumber
15

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:

import os
os.system('ts <your-command>')

Catatan tentang spooler tugas ( ts):

  1. Anda bisa mengatur jumlah proses bersamaan untuk dijalankan ("slot") dengan:

    ts -S <number-of-slots>

  2. Instalasi tstidak memerlukan hak admin. Anda dapat mengunduh dan mengompilasinya dari sumber dengan sederhana make, menambahkannya ke jalur Anda dan selesai.

Yuval Atzmon
sumber
1
tsbukan standar pada setiap distro yang saya tahu, meskipun pointer ke atagak berguna. Anda mungkin juga harus menyebutkan batch. Seperti di tempat lain, os.system()rekomendasi mungkin setidaknya harus menyebutkan bahwa itu subprocessadalah penggantian yang direkomendasikan.
tripleee
15

Anda dapat menggunakan Popen, dan kemudian Anda dapat memeriksa status prosedur:

from subprocess import Popen

proc = Popen(['ls', '-l'])
if proc.poll() is None:
    proc.kill()

Lihat subproses.Popen .

mengagumi
sumber
15

Untuk mengambil id jaringan dari 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)

Output dari daftar nova

+--------------------------------------+------------+------+
| 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 |
+--------------------------------------+------------+------+

Output cetak (networkId)

27a74fcd-37c0-4789-9414-9531b7e3f126
IRSHAD
sumber
Anda tidak boleh merekomendasikan os.popen()pada 2016. Skrip Awk dapat dengan mudah diganti dengan kode Python asli.
tripleee