Pertanyaan Memanggil perintah eksternal dengan Python


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


3622
2017-09-18 01:35


asal


Jawaban:


Lihatlah modul subprocess di pustaka standar:

from subprocess import call
call(["ls", "-l"])

Keuntungan dari subproses vs. sistem adalah bahwa itu lebih fleksibel (Anda bisa mendapatkan stdout, stderr, kode status "nyata", penanganan kesalahan yang lebih baik, dll ...).

Itu dokumentasi resmi merekomendasikan subproses modul di atas sistem alternatif os.system ():

Itu subproses Modul menyediakan fasilitas yang lebih kuat untuk menelurkan proses baru dan mengambil hasil mereka; menggunakan modul itu lebih baik menggunakan fungsi ini [os.system()].

"Mengganti Fungsi yang Lebih Lama dengan Modul subprocess"bagian dalam subproses dokumentasi mungkin memiliki beberapa resep yang bermanfaat.

Dokumentasi resmi tentang subproses modul:


3491
2017-09-18 01:39



Berikut adalah ringkasan cara untuk memanggil program eksternal dan keuntungan dan kerugiannya masing-masing:

  1. os.system("some_command with args") melewati perintah dan argumen ke shell sistem Anda. Ini bagus karena Anda benar-benar dapat menjalankan beberapa 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 nyaman, Anda harus secara manual menangani pelepasan karakter shell seperti spasi, dll. Di sisi lain, ini juga memungkinkan Anda menjalankan perintah yang hanya perintah shell dan bukan program eksternal. Lihat dokumentasi.

  2. stream = os.popen("some_command with args") akan melakukan hal yang sama os.system kecuali bahwa ia memberi Anda objek seperti file yang dapat Anda gunakan untuk mengakses input / output standar untuk proses itu. Ada 3 varian lain popen yang semuanya menangani i / o sedikit berbeda. Jika Anda melewatkan semuanya sebagai string, maka perintah Anda diteruskan ke shell; jika Anda meneruskannya sebagai daftar, Anda tidak perlu khawatir akan melarikan diri. Lihat dokumentasi.

  3. Itu Popen kelas dari subprocess modul. Ini dimaksudkan sebagai pengganti os.popen tetapi memiliki kelemahan karena sedikit lebih rumit karena sangat komprehensif. Misalnya, Anda akan berkata:

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

    dari pada:

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

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

  4. Itu call fungsi dari subprocess modul. Ini pada dasarnya sama seperti Popen kelas 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.

  5. Jika Anda menggunakan Python 3.5 atau yang lebih baru, Anda dapat menggunakan yang baru subprocess.run fungsi, yang banyak seperti di atas tetapi bahkan lebih fleksibel dan mengembalikan a CompletedProcess objek ketika perintah selesai dieksekusi.

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

Itu subprocess Modul mungkin harus menjadi apa yang Anda gunakan.

Akhirnya harap diperhatikan bahwa untuk semua metode di mana Anda melewati perintah akhir yang akan dieksekusi oleh shell sebagai string dan Anda bertanggung jawab untuk melarikan diri. Ada implikasi keamanan yang serius jika ada bagian dari string yang Anda lewati tidak dapat sepenuhnya dipercaya. Misalnya, jika pengguna memasukkan sebagian / bagian dari 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 masuk "mama saya tidak mencintaiku & & rm -rf /".


2466
2017-09-18 13:11



Saya biasanya menggunakan:

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 stdout data dalam pipa. Bahkan, Anda cukup menghilangkan parameter tersebut (stdout= dan stderr=) dan itu akan berperilaku seperti os.system().


255
2017-09-18 18:20



Beberapa petunjuk untuk melepaskan proses anak dari panggilan satu (memulai proses anak di latar belakang).

Misalnya Anda ingin memulai tugas panjang dari skrip CGI, yaitu proses anak harus hidup lebih lama daripada proses eksekusi skrip CGI.

Contoh klasik dari dokumen modul subprocess 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 'memanggil subprocess' sampai longtask.py selesai. Namun tidak jelas apa yang terjadi setelah baris 'beberapa kode lagi di sini' dari contoh.

Platform target saya adalah freebsd, tetapi pengembangannya ada di windows, jadi saya menghadapi masalah pada windows terlebih dahulu.

Pada windows (win xp), proses induk tidak akan selesai sampai longtask.py selesai bekerja. Ini bukan apa yang Anda inginkan di CGI-script. Masalahnya tidak khusus untuk Python, di komunitas PHP masalahnya sama.

Solusinya adalah dengan lulus DETACHED_PROCESS Proses Penciptaan Bendera ke fungsi CreateProcess yang mendasari dalam API menang. Jika Anda menginstal pywin32, Anda dapat mengimpor bendera 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 bendera semantik yang benar adalah CREATE_NEW_CONSOLE (0x00000010) * /

Pada freebsd kita memiliki masalah lain: ketika proses induk selesai, itu menyelesaikan proses anak juga. Dan itu bukan yang Anda inginkan dalam skrip CGI juga. Beberapa percobaan menunjukkan bahwa masalah itu tampaknya dalam berbagi sys.stdout. Dan solusi yang berfungsi adalah sebagai berikut:

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

Saya belum memeriksa kode pada platform lain dan tidak tahu alasan perilaku di freebsd. Jika ada yang tahu, tolong bagikan ide Anda. Googling pada memulai proses latar belakang dengan Python tidak menjelaskan sedikit pun.


157
2018-02-12 10:15



Saya akan merekomendasikan menggunakan modul subprocess daripada os.system karena shell melarikan diri untuk Anda dan karena itu jauh lebih aman: http://docs.python.org/library/subprocess.html

subprocess.call(['ping', 'localhost'])

97
2017-09-18 01:42



import os
cmd = 'ls -al'
os.system(cmd)

Jika Anda ingin mengembalikan hasil perintah, Anda dapat menggunakan os.popen. Namun, ini tidak lagi digunakan sejak versi 2.6 mendukung modul subprocess, yang jawaban lainnya telah dipenuhi dengan baik.


93
2017-09-18 01:37



import os
os.system("your command")

Perhatikan bahwa ini berbahaya, karena perintah tidak dibersihkan. Saya serahkan kepada Anda ke google untuk dokumentasi yang relevan pada modul 'os' dan 'sys'. Ada banyak fungsi (exec * dan spawn *) yang akan melakukan hal serupa.


82
2017-09-18 01:37



Saya selalu menggunakan fabric untuk hal-hal seperti ini:

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

Tapi ini sepertinya alat yang bagus: sh (Antarmuka subproses Python).

Lihatlah sebuah contoh:

from sh import vgdisplay
print vgdisplay()
print vgdisplay('-v')
print vgdisplay(v=True)

51
2018-03-13 00:12



Periksa pustaka "pexpect" Python juga.

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

child = pexpect.spawn('ftp 192.168.0.24')

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

child.sendline('anonymous')

child.expect('(?i)password')

51
2017-10-07 07:09