Cara melancarkan proses luaran dengan Python dan modul subprocess
- 2069
- 519
- Don Will
Dalam skrip automasi kami, kami sering perlu melancarkan dan memantau program luaran untuk menyelesaikan tugas yang kami inginkan. Semasa bekerja dengan Python, kita boleh menggunakan modul subprocess untuk melaksanakan operasi tersebut. Modul ini adalah sebahagian daripada perpustakaan standard bahasa pengaturcaraan. Dalam tutorial ini kita akan melihatnya dengan cepat, dan kita akan mempelajari asas -asas penggunaannya.
Dalam tutorial ini anda akan belajar:
- Cara menggunakan fungsi "lari" untuk menanam proses luaran
- Cara Menangkap Output Standard Proses dan Kesalahan Standard
- Cara Memeriksa Status Ada Proses dan Meningkatkan Pengecualian Sekiranya Gagal
- Cara Melaksanakan Proses ke Kotak Perantara
- Cara menetapkan masa untuk proses
- Cara menggunakan kelas popen terus ke paip dua proses
Keperluan perisian dan konvensyen yang digunakan
Kategori | Keperluan, konvensyen atau versi perisian yang digunakan |
---|---|
Sistem | Pengedaran bebas |
Perisian | Python3 |
Yang lain | Pengetahuan mengenai pengaturcaraan berorientasikan python dan objek |
Konvensyen | # - Memerlukan komando linux yang diberikan untuk dilaksanakan dengan keistimewaan akar sama ada secara langsung sebagai pengguna root atau dengan menggunakan sudo perintah$-memerlukan komando Linux yang diberikan sebagai pengguna yang tidak berkadar biasa |
Fungsi "lari"
The Jalankan fungsi telah ditambah ke subprocess Modul hanya dalam versi Python yang agak baru (3.5). Menggunakannya sekarang adalah cara yang disyorkan untuk menanam proses dan harus meliputi kes penggunaan yang paling biasa. Sebelum semua yang lain, mari kita lihat penggunaannya yang paling mudah. Katakan kita mahu menjalankan ls -al
perintah; Dalam shell python kita akan lari:
>>> SubProcess Import >>> Proses = Subprocess.Run (['ls', '-l', '-a'])
Output arahan luaran dipaparkan pada skrin:
Jumlah 132 DRWX------. 22 EGDOC EGDOC 4096 30 Nov 12:18 . DRWXR-XR-X. 4 akar root 4096 Nov 22 13: 11 ... -rw-------. 1 EGDOC EGDOC 10438 1 Dis 12:54 .BASH_HISTORY -RW-R-R--. 1 EGDOC EGDOC 18 Jul 27 15:10 .BASH_LOGOUT [...]
Di sini kita hanya menggunakan hujah mandatori yang pertama yang diterima oleh fungsi, yang boleh menjadi urutan yang "menggambarkan" perintah dan argumennya (seperti dalam contoh) atau rentetan, yang harus digunakan ketika berjalan dengan shell = benar
hujah (kita akan melihatnya kemudian).
Menangkap perintah stdout dan stderr
Bagaimana jika kita tidak mahu output proses dipaparkan di skrin, tetapi sebaliknya ditangkap, jadi ia boleh dirujuk selepas proses keluar? Dalam hal ini kita dapat menetapkan capture_output
hujah fungsi untuk Benar
:
>>> proses = subprocess.Run (['ls', '-l', '-a'], capture_output = true)
Bagaimana kita dapat mengambil output (stdout dan stderr) proses selepas itu? Sekiranya anda melihat contoh di atas, anda dapat melihat kami menggunakan proses
pemboleh ubah untuk merujuk apa yang dikembalikan oleh Jalankan
Fungsi: a Siap proses
objek. Objek ini mewakili proses yang dilancarkan oleh fungsi dan mempunyai banyak sifat berguna. Antara yang lain, stdout
dan stderr
digunakan untuk "menyimpan" deskriptor yang sepadan dengan arahan jika, seperti yang kita katakan, capture_output
Argumen ditetapkan ke Benar
. Dalam kes ini, untuk mendapatkan stdout
proses yang akan kami jalankan:
>>> proses.stdout
Stdout dan stderr disimpan sebagai urutan bait Secara lalai. Sekiranya kita mahu mereka disimpan sebagai rentetan, kita mesti menetapkan teks
hujah Jalankan
berfungsi untuk Benar
.
Menguruskan kegagalan proses
Perintah yang kami lari dalam contoh sebelumnya dilaksanakan tanpa kesilapan. Walau bagaimanapun, semasa menulis program, semua kes perlu diambil kira, jadi bagaimana jika proses yang dilahirkan gagal? Secara lalai tidak ada yang "istimewa" akan berlaku. Mari lihat contoh; Kami menjalankan ls
perintah sekali lagi, cuba menyenaraikan kandungan /root
Direktori, yang biasanya, pada Linux tidak boleh dibaca oleh pengguna biasa:
>>> proses = subprocess.Run (['ls', '-l', '-a', '/root'])
Satu perkara yang boleh kita lakukan untuk memeriksa sama ada proses yang dilancarkan gagal, adalah untuk memeriksa status wujudnya, yang disimpan di Kod Return
harta benda Siap proses
objek:
>>> proses.ReturnCode 2
Lihat? Dalam kes ini Kod Return adalah 2
, mengesahkan bahawa proses itu menghadapi masalah kebenaran, dan tidak berjaya selesai. Kita dapat menguji output proses dengan cara ini, atau lebih elegan kita dapat membuat supaya pengecualian dinaikkan apabila gagal berlaku. Masuk ke periksa
hujah Jalankan
Fungsi: Apabila ditetapkan ke Benar
dan proses yang melahirkan gagal, DipanggilProcessError
Pengecualian dibangkitkan:
>>> proses = subprocess.Run (['ls', '-l', '-a', '/root'], check = true) ls: tidak dapat membuka direktori '/root': kebenaran menafikan traceback (panggilan terakhir terakhir): fail "" , baris 1, dalam fail "/usr/lib64/python3.9/subprocess.py ", baris 524, dalam Run Raise Rause yang dipanggilProcessError (Retcode, Proses.Args, subprocess.DipanggilProcessError: command '[' ls ',' -l ',' -a ','/root ']' kembali status keluar bukan sifar 2.
Pengendalian pengecualian Di Python agak mudah, jadi untuk menguruskan kegagalan proses kita boleh menulis sesuatu seperti:
>>> Cuba: ... proses = subprocess.Run (['ls', '-l', '-a', '/root'], check = true) ... kecuali subprocess.DipanggilProcessError sebagai e: ... # Contohnya, sesuatu yang berguna untuk menguruskan kegagalan harus dilakukan!... cetak (f "e.cmd gagal!") ... ls: tidak dapat membuka direktori '/root': kebenaran dinafikan ['ls', '-l', '-a', '/root'] Gagal! >>>
The DipanggilProcessError
pengecualian, seperti yang kita katakan, dibangkitkan apabila proses keluar dengan bukan 0
status. Objek mempunyai sifat seperti Kod Return
, cmd
, stdout
, stderr
; Apa yang mereka wakili cukup jelas. Dalam contoh di atas, misalnya, kami hanya menggunakan cmd
harta, untuk melaporkan urutan yang digunakan untuk menggambarkan arahan dan hujah -hujahnya dalam mesej yang kami tulis apabila pengecualian berlaku.
Laksanakan proses dalam cengkerang
Proses yang dilancarkan dengan Jalankan
fungsi, dilaksanakan "secara langsung", ini bermakna bahawa tiada shell digunakan untuk melancarkannya: tidak ada pembolehubah persekitaran yang tersedia untuk proses dan pengembangan shell tidak dilakukan. Mari kita lihat contoh yang melibatkan penggunaan $ Rumah
berubah -ubah:
>>> proses = subprocess.Run (['ls', '-al', '$ home']) ls: tidak dapat mengakses '$ home': tiada fail atau direktori sedemikian
Seperti yang anda dapat lihat $ Rumah
Pembolehubah tidak berkembang. Melaksanakan proses dengan cara ini disyorkan supaya dapat mengelakkan potensi risiko keselamatan. Sekiranya dalam kes -kes tertentu, kita perlu menggunakan shell sebagai proses perantaraan yang kita perlukan untuk menetapkan shell
parameter Jalankan
berfungsi untuk Benar
. Dalam kes sedemikian lebih baik untuk menentukan arahan yang akan dilaksanakan dan hujahnya sebagai tali:
>>> proses = subprocess.Run ('ls -al $ home', shell = true) total 136 drwx------. 23 EGDOC EGDOC 4096 3 Dis 09:35 . DRWXR-XR-X. 4 akar root 4096 Nov 22 13: 11 ... -rw-------. 1 EGDOC EGDOC 11885 3 Dis 09:35 .BASH_HISTORY -RW-R-R--. 1 EGDOC EGDOC 18 Jul 27 15:10 .BASH_LOGOUT [...]
Semua pembolehubah yang ada dalam persekitaran pengguna boleh digunakan apabila menggunakan shell sebagai proses perantaraan: sementara ini dapat kelihatan berguna, ia boleh menjadi sumber masalah, terutama ketika berurusan dengan input yang berpotensi berbahaya, yang dapat menyebabkan suntikan shell. Menjalankan proses dengan shell = benar
Oleh itu, tidak digalakkan, dan harus digunakan hanya dalam kes yang selamat.
Menentukan masa tamat untuk proses
Kami biasanya tidak mahu proses salah laku berjalan selama -lamanya di sistem kami setelah dilancarkan. Sekiranya kita menggunakan masa tamat
parameter Jalankan
fungsi, kita dapat menentukan jumlah masa dalam beberapa saat prosesnya perlu dilakukan. Sekiranya ia tidak selesai dalam jumlah masa itu, proses itu akan dibunuh dengan Sigkill isyarat, yang, seperti yang kita tahu, tidak dapat ditangkap oleh proses. Mari kita tunjukkannya dengan memajukan proses berjalan yang panjang dan memberikan masa tamat dalam beberapa saat:
>>> proses = subprocess.Jalankan (['Ping', 'Google.com '], timeout = 5) ping google.com (216.58.206.46) 56 (84) bait data. 64 bait dari MIL07S07-in-F14.1E100.bersih (216.58.206.46): icmp_seq = 1 ttl = 113 masa = 29.3 ms 64 bait dari LHR35S10-in-F14.1E100.bersih (216.58.206.46): icmp_seq = 2 ttl = 113 time = 28.3 ms 64 bait dari LHR35S10-in-F14.1E100.bersih (216.58.206.46): icmp_seq = 3 ttl = 113 masa = 28.5 ms 64 bait dari LHR35S10-in-F14.1E100.bersih (216.58.206.46): icmp_seq = 4 ttl = 113 masa = 28.5 ms 64 bait dari LHR35S10-in-F14.1E100.bersih (216.58.206.46): icmp_seq = 5 ttl = 113 masa = 28.1 ms traceback (panggilan terakhir terakhir): fail "", baris 1, dalam fail "/usr/lib64/python3.9/subprocess.py ", baris 503, dalam larian stdout, stderr = proses.berkomunikasi (input, timeout = timeout) fail "/usr/lib64/python3.9/subprocess.py ", baris 1130, dalam berkomunikasi stdout, stderr = diri._Communicate (Input, Endtime, Timeout) Fail "/usr/lib64/python3.9/subprocess.py ", line 2003, dalam _Communicate Diri.tunggu (tamat masa = diri._remaining_time (endtime)) Fail "/usr/lib64/python3.9/subprocess.py ", baris 1185, dalam tunggu kembali diri._wait (timeout = timeout) fail "/usr/lib64/python3.9/subprocess.py ", baris 1907, dalam _wait menaikkan timeoutExpired (diri.Subprocess Args, Timeout).TimeoutExpired: Command '[' Ping ',' Google.com ']' masa keluar selepas 4.999826977029443 saat
Dalam contoh di atas kami melancarkan ping
perintah tanpa menentukan jumlah tetap Permintaan echo paket, oleh itu ia berpotensi berlari selama -lamanya. Kami juga menetapkan masa tamat 5
detik melalui masa tamat
parameter. Seperti yang kita dapat melihat program pada mulanya dijalankan, tetapi Timeoutexpired
Pengecualian dinaikkan apabila jumlah detik yang ditentukan telah dicapai, dan prosesnya dibunuh.
Fungsi panggilan, check_output dan check_call
Seperti yang kita katakan sebelum ini, Jalankan
Fungsi adalah cara yang disyorkan untuk menjalankan proses luaran dan harus meliputi majoriti kes. Sebelum ia diperkenalkan di Python 3.5, tiga fungsi API peringkat tinggi utama yang digunakan untuk melancarkan proses adalah Panggilan
, check_output
dan check_call
; Mari lihat mereka secara ringkas.
Pertama sekali, Panggilan
Fungsi: Ia digunakan untuk menjalankan arahan yang diterangkan oleh Args
parameter; ia menunggu perintah itu selesai dan mengembalikannya Kod Return. Ia secara kasar sepadan dengan penggunaan asas Jalankan
fungsi.
The check_call
tingkah laku fungsi hampir sama dengan Jalankan
berfungsi apabila periksa
parameter ditetapkan ke Benar
: ia menjalankan perintah yang ditentukan dan menantinya menyelesaikannya. Sekiranya status wujudnya tidak 0
, a DipanggilProcessError
Pengecualian dibangkitkan.
Akhirnya, check_output
Fungsi: Ia berfungsi sama seperti check_call
, tetapi pulangan output program: ia tidak dipaparkan apabila fungsi dilaksanakan.
Bekerja pada tahap yang lebih rendah dengan kelas popen
Sehingga kini kami meneroka fungsi API peringkat tinggi dalam modul subprocess, terutamanya Jalankan
. Semua fungsi ini, di bawah tudung berinteraksi dengan Popen
kelas. Oleh sebab itu, dalam kebanyakan kes kita tidak perlu bekerja dengannya secara langsung. Walau bagaimanapun, apabila lebih banyak fleksibiliti diperlukan Popen
objek secara langsung diperlukan.
Katakan, sebagai contoh, kami ingin menyambungkan dua proses, mencipta tingkah laku "paip" shell. Seperti yang kita ketahui, apabila kita paip dua arahan di dalam shell, output standard yang ada di sebelah kiri paip (|
) digunakan sebagai input standard yang ada di sebelah kanannya (periksa artikel ini mengenai pengalihan shell jika anda ingin mengetahui lebih lanjut mengenai subjek). Dalam contoh di bawah hasil paip kedua -dua arahan disimpan dalam pembolehubah:
$ output = "$ (dmesg | grep sda)"
Untuk mencontohi tingkah laku ini menggunakan modul subprocess, tanpa perlu menetapkan shell
parameter ke Benar
Seperti yang kita lihat sebelum ini, kita mesti menggunakan Popen
kelas secara langsung:
dmesg = subprocess.Popen (['dmesg'], stdout = subprocess.Paip) grep = subprocess.Popen (['grep', 'sda'], stdin = dmesg.stdout) dmesg.stdout.tutup () output = grep.comunicate () [0]
Untuk memahami contoh di atas, kita harus ingat bahawa proses bermula dengan menggunakan Popen
kelas secara langsung tidak menghalang pelaksanaan skrip, kerana kini menunggu.
Perkara pertama yang kami lakukan dalam coretan kod di atas, adalah untuk membuat Popen
objek yang mewakili DMESG proses. Kami menetapkan stdout
proses ini untuk subprocess.Paip
: Nilai ini menunjukkan bahawa paip ke aliran yang ditentukan harus dibuka.
Kita daripada mencipta contoh lain dari Popen
kelas untuk grep proses. Di dalam Popen
Pembina Kami Menentukan Perintah dan Argumennya, Sudah tentu, tetapi, inilah bahagian penting, kami menetapkan output standard DMESG proses yang akan digunakan sebagai input standard (stdin = dmesg.stdout
), jadi untuk mencipta cangkang
tingkah laku paip.
Selepas membuat Popen
objek untuk grep perintah, kami menutup stdout
aliran DMESG proses, menggunakan Tutup ()
Kaedah: Ini, seperti yang dinyatakan dalam dokumentasi, diperlukan untuk membolehkan proses pertama menerima isyarat sigpipe. Mari cuba jelaskan mengapa. Biasanya, apabila dua proses disambungkan oleh paip, jika satu di sebelah kanan paip (grep dalam contoh kami) keluar sebelum satu di sebelah kiri (dmesg), yang terakhir menerima a Sigpipe
isyarat (paip pecah) dan secara lalai, menamatkan dirinya.
Apabila mereplikasi tingkah laku paip antara dua arahan di Python, bagaimanapun, ada masalah: stdout proses pertama dibuka di skrip induk dan dalam input standard proses lain. Dengan cara ini, walaupun grep proses berakhir, paip masih akan tetap terbuka dalam proses pemanggil (skrip kami), oleh itu proses pertama tidak akan menerima Sigpipe isyarat. Inilah sebabnya kita perlu menutup stdout aliran proses pertama di kami
skrip utama setelah kami melancarkan yang kedua.
Perkara terakhir yang kami lakukan ialah memanggil berkomunikasi ()
kaedah pada grep objek. Kaedah ini boleh digunakan untuk lulus input secara pilihan ke proses; ia menunggu proses untuk menamatkan dan mengembalikan tuple di mana ahli pertama adalah prosesnya stdout (yang dirujuk oleh pengeluaran
pemboleh ubah) dan kedua prosesnya stderr.
Kesimpulan
Dalam tutorial ini, kita melihat cara yang disyorkan untuk menanam proses luaran dengan Python menggunakan subprocess modul dan Jalankan
fungsi. Penggunaan fungsi ini sepatutnya cukup untuk majoriti kes; Apabila tahap fleksibiliti yang lebih tinggi diperlukan, bagaimanapun, seseorang mesti menggunakan Popen
kelas secara langsung. Seperti biasa, kami mencadangkan untuk melihat
dokumentasi subprocess untuk gambaran lengkap mengenai tandatangan fungsi dan kelas yang terdapat di
modul.
Tutorial Linux Berkaitan:
- Pengenalan kepada Automasi, Alat dan Teknik Linux
- Menguasai Gelung Skrip Bash
- Perkara yang hendak dipasang di Ubuntu 20.04
- Gelung bersarang dalam skrip bash
- Cara Menggunakan Perintah TCPDUMP di Linux
- Mint 20: Lebih baik daripada Ubuntu dan Microsoft Windows?
- Mengendalikan input pengguna dalam skrip bash
- Tutorial Debugging GDB untuk Pemula
- Perkara yang perlu dilakukan setelah memasang ubuntu 20.04 Focal Fossa Linux
- Cara Memantau Aktiviti Rangkaian pada Sistem Linux