Cara meluncurkan proses eksternal dengan python dan modul subproses

Cara meluncurkan proses eksternal dengan python dan modul subproses

Dalam skrip otomatisasi kami, kami sering perlu meluncurkan dan memantau program eksternal untuk menyelesaikan tugas yang kami inginkan. Saat bekerja dengan Python, kami dapat menggunakan modul subproses untuk melakukan operasi tersebut. Modul ini adalah bagian dari perpustakaan standar bahasa pemrograman. Dalam tutorial ini kita akan melihatnya dengan cepat, dan kita akan mempelajari dasar -dasar penggunaannya.

Dalam tutorial ini Anda akan belajar:

  • Cara menggunakan fungsi "jalankan" untuk menelurkan proses eksternal
  • Cara menangkap output standar proses dan kesalahan standar
  • Bagaimana memeriksa status proses yang ada dan menaikkan pengecualian jika gagal
  • Cara menjalankan proses ke dalam cangkang perantara
  • Cara mengatur batas waktu untuk suatu proses
  • Cara menggunakan kelas Popen secara langsung untuk menyalurkan dua proses
Cara meluncurkan proses eksternal dengan python dan modul subproses

Persyaratan dan konvensi perangkat lunak yang digunakan

Persyaratan Perangkat Lunak dan Konvensi Baris Perintah Linux
Kategori Persyaratan, konvensi atau versi perangkat lunak yang digunakan
Sistem Distribusi Independen
Perangkat lunak Python3
Lainnya Pengetahuan tentang Python dan pemrograman berorientasi objek
Konvensi # - mensyaratkan Linux -Commands untuk dieksekusi dengan hak istimewa root baik secara langsung sebagai pengguna root atau dengan menggunakan sudo memerintah
$-mensyaratkan Linux-Commands untuk dieksekusi sebagai pengguna reguler yang tidak istimewa

Fungsi "Jalankan"

Itu berlari Fungsi telah ditambahkan ke subproses Modul hanya dalam versi Python yang relatif baru (3.5). Menggunakannya sekarang adalah cara yang disarankan untuk menelurkan proses dan harus mencakup kasus penggunaan yang paling umum. Sebelum yang lainnya, mari kita lihat penggunaannya yang paling sederhana. Misalkan kita ingin menjalankan ls -al memerintah; Dalam cangkang ular surut kami akan menjalankan:

>>> Impor Subprocess >>> Proses = Subprocess.Jalankan (['ls', '-l', '-a']) 

Output dari perintah eksternal ditampilkan di layar:

Total 132 DRWX------. 22 EGDOC EGDOC 4096 30 Nov 12:18 . DRWXR-XR-X. 4 root root 4096 Nov 22 13: 11… -rw-------. 1 EGDOC EGDOC 10438 Des 1 12:54 .BASH_HISTORY -RW-R-R--. 1 EGDOC EGDOC 18 Jul 27 15:10 .Bash_logout […] 

Di sini kami hanya menggunakan argumen wajib pertama yang diterima oleh fungsi, yang dapat berupa urutan yang "menggambarkan" perintah dan argumennya (seperti dalam contoh) atau string, yang harus digunakan saat berjalan dengan dengan shell = true argumen (kita akan melihatnya nanti).

Menangkap perintah stdout dan stderr

Bagaimana jika kita tidak ingin output dari proses ditampilkan di layar, tetapi sebaliknya ditangkap, sehingga dapat direferensikan setelah proses keluar? Dalam hal ini kita dapat mengatur capture_output argumen fungsi untuk BENAR:

>>> Proses = Subproses.Jalankan (['ls', '-l', '-a'], capture_output = true) 

Bagaimana kita bisa mengambil output (stdout dan stderr) dari proses sesudahnya? Jika Anda mengamati contoh di atas, Anda dapat melihat kami menggunakan proses variabel untuk merujuk apa yang dikembalikan oleh berlari Fungsi: a Proses Selesai obyek. Objek ini mewakili proses yang diluncurkan oleh fungsi dan memiliki banyak sifat yang berguna. Di antara yang lain, stdout Dan Stderr digunakan untuk “menyimpan” deskriptor yang sesuai dari perintah jika, seperti yang kami katakan, capture_output Argumen diatur ke BENAR. Dalam hal ini, untuk mendapatkan stdout dari proses yang akan kami jalankan:

>>> Proses.stdout 

Stdout dan stderr disimpan sebagai Urutan byte secara default. Jika kita ingin mereka disimpan sebagai string, kita harus mengatur teks argumen dari berlari berfungsi BENAR.



Kelola Kegagalan Proses

Perintah yang kami jalankan dalam contoh sebelumnya dieksekusi tanpa kesalahan. Saat menulis program, bagaimanapun, semua kasus harus dipertimbangkan, jadi bagaimana jika proses melahirkan gagal? Secara default tidak ada "spesial" yang akan terjadi. Mari kita lihat contoh; kami menjalankan ls Perintah lagi, mencoba mencantumkan konten dari /akar Direktori, yang biasanya, di Linux tidak dapat dibaca oleh pengguna normal:

>>> Proses = Subproses.Jalankan (['ls', '-l', '-a', '/root']) 

Satu hal yang dapat kami lakukan untuk memeriksa apakah proses yang diluncurkan gagal, adalah memeriksa status yang ada, yang disimpan di ReturnCode properti dari Proses Selesai obyek:

>>> Proses.ReturnCode 2 

Melihat? Dalam hal ini ReturnCode dulu 2, mengkonfirmasi bahwa proses tersebut mengalami masalah izin, dan tidak berhasil diselesaikan. Kita bisa menguji output dari suatu proses dengan cara ini, atau lebih elegan yang bisa kita buat sehingga pengecualian dinaikkan ketika kegagalan terjadi. Masukkan memeriksa argumen dari berlari Fungsi: Saat diatur ke BENAR dan proses yang melahirkan gagal, CalledProcessError Pengecualian dinaikkan:

>>> Proses = Subproses.Jalankan (['ls', '-l', '-a', '/root'], periksa = true) ls: tidak dapat membuka direktori '/root': izin ditolak traceback (panggilan terbaru terakhir): file "" , baris 1, dalam file "/usr/lib64/python3.9/Subproses.py ", baris 524, dalam run raise calledProcessError (retcode, proses.Args, Subprocess.CalledProcessError: Command '[' ls ',' -l ',' -a ','/root ']' Returned Non Nol Status Exit 2. 

Penanganan pengecualian Dalam Python cukup mudah, jadi untuk mengelola kegagalan proses kita bisa menulis sesuatu seperti:

>>> Coba:… Proses = Subproses.Jalankan (['ls', '-l', '-a', '/root'], periksa = true) ... kecuali subproses.CalledProcessError sebagai E: ... # Contoh saja, sesuatu yang berguna untuk mengelola kegagalan harus dilakukan!… Print (f "e.CMD gagal!") ... ls: tidak dapat membuka direktori '/root': izin ditolak ['ls', '-l', '-a', '/root'] Gagal! >>> 

Itu CalledProcessError Pengecualian, seperti yang kami katakan, dinaikkan ketika suatu proses keluar dengan non 0 status. Objek memiliki properti seperti ReturnCode, cmd, stdout, Stderr; Apa yang mereka wakili cukup jelas. Dalam contoh di atas, misalnya, kami hanya menggunakan cmd properti, untuk melaporkan urutan yang digunakan untuk menggambarkan perintah dan argumennya dalam pesan yang kami tulis ketika pengecualian terjadi.

Jalankan proses dalam shell

Proses diluncurkan dengan berlari fungsi, dijalankan "langsung", ini berarti bahwa tidak ada shell yang digunakan untuk meluncurkannya: karena itu tidak ada variabel lingkungan yang tersedia untuk proses dan ekspansi shell tidak dilakukan. Mari kita lihat contoh yang melibatkan penggunaan $ Rumah variabel:

>>> Proses = Subproses.Jalankan (['ls', '-al', '$ home']) ls: tidak dapat mengakses '$ home': tidak ada file atau direktori seperti itu 

Seperti yang Anda lihat $ Rumah Variabel tidak diperluas. Mengeksekusi proses dengan cara ini direkomendasikan untuk menghindari potensi risiko keamanan. Namun, jika dalam kasus tertentu, kita perlu memohon cangkang sebagai proses perantara, kita perlu mengatur kerang parameter berlari berfungsi BENAR. Dalam kasus seperti itu lebih baik untuk menentukan perintah yang akan dieksekusi dan argumennya sebagai a rangkaian:

>>> Proses = Subproses.Jalankan ('ls -al $ home', shell = true) Total 136 DRWX------. 23 EGDOC EGDOC 4096 Des 3 09:35 . DRWXR-XR-X. 4 root root 4096 Nov 22 13: 11… -rw-------. 1 EGDOC EGDOC 11885 Des 3 09:35 .BASH_HISTORY -RW-R-R--. 1 EGDOC EGDOC 18 Jul 27 15:10 .Bash_logout […] 

Semua variabel yang ada di lingkungan pengguna dapat digunakan saat memanggil shell sebagai proses perantara: walaupun ini bisa terlihat berguna, itu bisa menjadi sumber masalah, terutama ketika berhadapan dengan input yang berpotensi berbahaya, yang dapat menyebabkan suntikan shell. Menjalankan proses dengan shell = true Oleh karena itu berkecil hati, dan harus digunakan hanya dalam kasus yang aman.



Menentukan batas waktu untuk suatu proses

Kami biasanya tidak ingin proses yang salah untuk berjalan selamanya di sistem kami begitu mereka diluncurkan. Jika kita menggunakan waktu habis parameter berlari Fungsi, kita dapat menentukan jumlah waktu dalam detik proses yang harus dilakukan untuk diselesaikan. Jika tidak selesai dalam jumlah waktu itu, prosesnya akan dibunuh dengan a Sigkill sinyal, yang, seperti yang kita ketahui, tidak dapat ditangkap oleh suatu proses. Mari kita tunjukkan dengan memunculkan proses berjalan yang lama dan memberikan waktu tunggu dalam hitungan detik:

>>> Proses = Subproses.Jalankan (['ping', 'google.com '], timeout = 5) ping google.com (216.58.206.46) 56 (84) byte data. 64 byte dari MIL07S07-IN-F14.1E100.net (216.58.206.46): icmp_seq = 1 ttl = 113 waktu = 29.3 ms 64 byte dari lhr35s10-in-f14.1E100.net (216.58.206.46): icmp_seq = 2 ttl = 113 waktu = 28.3 ms 64 byte dari lhr35s10-in-f14.1E100.net (216.58.206.46): icmp_seq = 3 ttl = 113 waktu = 28.5 ms 64 byte dari lhr35s10-in-f14.1E100.net (216.58.206.46): icmp_seq = 4 ttl = 113 waktu = 28.5 ms 64 byte dari lhr35s10-in-f14.1E100.net (216.58.206.46): icmp_seq = 5 ttl = 113 waktu = 28.1 ms traceback (panggilan terbaru terakhir): file "", baris 1, dalam file "/usr/lib64/python3.9/Subproses.py ", baris 503, di run stdout, stderr = proses.Komunikasi (input, timeout = timeout) file "/usr/lib64/python3.9/Subproses.py ", baris 1130, dalam komunikasi stdout, stderr = self._Communicate (input, endtime, timeout) file "/usr/lib64/python3.9/Subproses.py ", line 2003, dalam _komunikasi diri.tunggu (timeout = self._remaining_time (endtime)) file "/usr/lib64/python3.9/Subproses.py ", baris 1185, menunggu kembali diri._wait (timeout = timeout) file "/usr/lib64/python3.9/Subproses.py ", line 1907, di _wait meningkatkan waktu habis (self.args, timeout) Subproses.TimeOutExpired: Perintah '[' ping ',' google.com ']' berjangka waktu setelah 4.999826977029443 detik 

Dalam contoh di atas kami meluncurkan ping Perintah tanpa menentukan jumlah tetap Permintaan gema paket, oleh karena itu berpotensi berjalan selamanya. Kami juga menentukan waktu tunggu 5 detik melalui waktu habis parameter. Karena kita dapat mengamati program awalnya berjalan, tetapi Waktu habis pengecualian dinaikkan ketika jumlah detik yang ditentukan tercapai, dan prosesnya terbunuh.

Fungsi panggilan, check_output dan check_call

Seperti yang kami katakan sebelumnya, berlari Fungsi adalah cara yang disarankan untuk menjalankan proses eksternal dan harus mencakup sebagian besar kasus. Sebelum diperkenalkan di Python 3.5, tiga fungsi API tingkat tinggi utama yang digunakan untuk meluncurkan proses adalah panggilan, check_output Dan check_call; Mari kita lihat secara singkat.

Pertama -tama, panggilan Fungsi: digunakan untuk menjalankan perintah yang dijelaskan oleh args parameter; itu menunggu perintah untuk diselesaikan dan mengembalikannya ReturnCode. Itu secara kasar sesuai dengan penggunaan dasar dari berlari fungsi.

Itu check_call perilaku fungsi praktis sama dengan yang dari berlari berfungsi saat memeriksa Parameter diatur ke BENAR: Ini menjalankan perintah yang ditentukan dan menunggu untuk menyelesaikannya. Jika statusnya tidak ada 0, A CalledProcessError pengecualian dinaikkan.

Akhirnya, check_output Fungsi: Ini berfungsi serupa dengan check_call, Tetapi kembali Output program: tidak ditampilkan saat fungsi dieksekusi.

Bekerja di level yang lebih rendah dengan kelas Popen

Sampai sekarang kami menjelajahi fungsi API tingkat tinggi dalam modul subproses, khususnya berlari. Semua fungsi ini, di bawah kap berinteraksi dengan Popen kelas. Karena itu, dalam sebagian besar kasus kita tidak harus bekerja dengannya secara langsung. Namun, ketika lebih banyak fleksibilitas diperlukan Popen objek secara langsung menjadi perlu.



Misalkan, misalnya, kita ingin menghubungkan dua proses, menciptakan kembali perilaku "pipa" shell ". Seperti yang kita ketahui, ketika kita menyalurkan dua perintah di shell, output standar dari yang ada di sisi kiri pipa (|) digunakan sebagai input standar dari yang ada di sebelah kanannya (periksa artikel ini tentang pengalihan shell jika Anda ingin tahu lebih banyak tentang subjek). Dalam contoh di bawah hasil perpipaan kedua perintah disimpan dalam variabel:

$ output = "$ (dmesg | grep sda)" 

Untuk meniru perilaku ini menggunakan modul subproses, tanpa harus mengatur kerang parameter ke BENAR Seperti yang kita lihat sebelumnya, kita harus menggunakan Popen Kelas Langsung:

DMESG = Subproses.POPEN (['DMESG'], STDOUT = Subprocess.Pipa) grep = subproses.Popen (['grep', 'sda'], stdin = dmesg.stdout) dmesg.stdout.tutup () output = grep.comunicate () [0] 

Untuk memahami contoh di atas kita harus ingat bahwa suatu proses dimulai dengan menggunakan Popen kelas secara langsung tidak memblokir eksekusi skrip, karena sekarang menunggu.

Hal pertama yang kami lakukan di cuplikan kode di atas, adalah membuat Popen objek yang mewakili dmesg proses. Kami mengatur stdout dari proses ini untuk subproses.PIPA: Nilai ini menunjukkan bahwa pipa ke aliran yang ditentukan harus dibuka.

Kami daripada membuat contoh lain dari Popen kelas untuk grep proses. Dalam Popen Konstruktor Kami menentukan perintah dan argumennya, tentu saja, tetapi, di sini adalah bagian yang penting, kami menetapkan output standar dari dmesg proses yang akan digunakan sebagai input standar (stdin = dmesg.stdout), jadi untuk menciptakan kembali shell
perilaku pipa.

Setelah membuat Popen objek untuk grep perintah, kami menutup stdout aliran dmesg proses, menggunakan menutup() Metode: Ini, sebagaimana dinyatakan dalam dokumentasi, diperlukan untuk memungkinkan proses pertama menerima sinyal sigpipe. Mari kita coba jelaskan mengapa. Biasanya, ketika dua proses dihubungkan dengan pipa, jika satu di sebelah kanan pipa (grep dalam contoh kami) keluar sebelum yang di kiri (DMESG), yang terakhir menerima a Sigpipe
sinyal (pipa rusak) dan secara default, berakhir dengan dirinya sendiri.

Saat mereplikasi perilaku pipa antara dua perintah dalam python, bagaimanapun, ada masalah: stdout dari proses pertama dibuka baik dalam skrip induk dan dalam input standar dari proses lainnya. Dengan cara ini, bahkan jika grep proses berakhir, pipa masih akan tetap terbuka dalam proses penelepon (skrip kami), oleh karena itu proses pertama tidak akan pernah menerima Sigpipe sinyal. Inilah sebabnya kami perlu menutup stdout aliran proses pertama di kami
skrip utama setelah kami meluncurkan yang kedua.

Hal terakhir yang kami lakukan adalah memanggil menyampaikan() metode pada grep obyek. Metode ini dapat digunakan untuk secara opsional meneruskan input ke suatu proses; itu menunggu proses untuk mengakhiri dan mengembalikan tuple di mana anggota pertama adalah prosesnya stdout (yang dirujuk oleh keluaran variabel) dan yang kedua prosesnya Stderr.

Kesimpulan

Dalam tutorial ini kami melihat cara yang disarankan untuk menelurkan proses eksternal dengan python menggunakan subproses modul dan berlari fungsi. Penggunaan fungsi ini harus cukup untuk sebagian besar kasus; Namun, ketika tingkat fleksibilitas yang lebih tinggi diperlukan, seseorang harus menggunakan Popen kelas secara langsung. Seperti biasa, kami menyarankan untuk melihat
Dokumentasi Subproses untuk Tinjauan Lengkap Tanda Tangan Fungsi dan Kelas Tersedia Dalam
modul.

Tutorial Linux Terkait:

  • Pengantar Otomatisasi Linux, Alat dan Teknik
  • Menguasai loop skrip bash
  • Hal -hal yang harus diinstal pada ubuntu 20.04
  • Loop bersarang dalam skrip bash
  • Cara menggunakan perintah tcpdump di linux
  • Mint 20: Lebih baik dari Ubuntu dan Microsoft Windows?
  • Menangani input pengguna dalam skrip bash
  • Tutorial debugging GDB untuk pemula
  • Hal -hal yang harus dilakukan setelah menginstal ubuntu 20.04 FOSSA FOSSA Linux
  • Cara memantau aktivitas jaringan pada sistem Linux