Cara melancarkan proses luaran dengan Python dan modul subprocess

Cara melancarkan proses luaran dengan Python dan modul subprocess

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
Cara melancarkan proses luaran dengan Python dan modul subprocess

Keperluan perisian dan konvensyen yang digunakan

Keperluan Perisian dan Konvensyen Talian Perintah Linux
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