Di mesin lokal saya, saya menjalankan skrip python yang berisi baris ini
bashCommand = "cwm --rdf test.rdf --ntriples > test.nt"
os.system(bashCommand)
Ini berfungsi dengan baik.
Kemudian saya menjalankan kode yang sama di server dan saya mendapatkan pesan kesalahan berikut
'import site' failed; use -v for traceback
Traceback (most recent call last):
File "/usr/bin/cwm", line 48, in <module>
from swap import diag
ImportError: No module named swap
Jadi apa yang saya lakukan kemudian adalah saya memasukkan print bashCommand
yang mencetak saya daripada perintah di terminal sebelum menjalankannya os.system()
.
Tentu saja, saya mendapatkan kembali kesalahan (disebabkan oleh os.system(bashCommand)
) tetapi sebelum kesalahan itu mencetak perintah di terminal. Kemudian saya hanya menyalin output itu dan melakukan copy paste ke terminal dan tekan enter dan berfungsi ...
Adakah yang tahu apa yang sedang terjadi?
cwm
. Mungkin Anda memiliki beberapa konfigurasi di lingkungan Anda.bashrc
yang mengatur lingkungan untuk penggunaan bash interaktif?cwm
. Atau mungkin ada perbedaan dalam PATH, dan versi yang berbedacwm
disebut. Atau versi Python yang berbeda. Sangat sulit untuk mengetahui hal ini tanpa akses ke mesin ...Jawaban:
Jangan gunakan
os.system
. Itu telah ditinggalkan demi subproses . Dari docs : "Modul ini bermaksud untuk menggantikan beberapa lebih tua modul dan fungsi:os.system
,os.spawn
".Seperti dalam kasus Anda:
sumber
cd 'path\to\somewhere'
diikuti oleh perintah bash lain yang perlu dijalankan di suatu tempat. @ user225312cwd
argumen untuk Popen:subprocess.Popen(..., cwd='path\to\somewhere')
stdout=file
mengalihkan output ke file dalam kasus ini. Ini mengimplementasikan> file
). Adalah keliru untuk melewatkan..., '>', 'file']
perintah terakhir yang mengharapkan pengalihan (itu tidak akan bekerja tanpa shell dan jika Anda menggunakan shell, Anda harus melewati perintah sebagai string)Untuk sedikit memperluas jawaban sebelumnya di sini, ada sejumlah detail yang biasanya diabaikan.
subprocess.run()
lebihsubprocess.check_call()
dan teman-teman selamasubprocess.call()
lebihsubprocess.Popen()
lebihos.system()
lebihos.popen()
text=True
, aliasuniversal_newlines=True
.shell=True
ataushell=False
dan bagaimana hal itu mengubah penawaran dan ketersediaan kenyamanan shell.sh
dan BashTopik-topik ini dibahas lebih rinci di bawah ini.
Lebih suka
subprocess.run()
atausubprocess.check_call()
Itu
subprocess.Popen()
Fungsi adalah pekerja keras tingkat rendah tetapi sulit untuk digunakan dengan benar dan Anda berakhir copy / paste beberapa baris kode ... yang nyaman sudah ada di perpustakaan standar sebagai satu set tingkat yang lebih tinggi wrapper fungsi untuk berbagai keperluan, yang disajikan secara lebih rinci sebagai berikut.Berikut paragraf dari dokumentasi :
Sayangnya, ketersediaan fungsi pembungkus ini berbeda antara versi Python.
subprocess.run()
secara resmi diperkenalkan di Python 3.5. Ini dimaksudkan untuk mengganti semua yang berikut ini.subprocess.check_output()
diperkenalkan di Python 2.7 / 3.1. Ini pada dasarnya setara dengansubprocess.run(..., check=True, stdout=subprocess.PIPE).stdout
subprocess.check_call()
diperkenalkan dalam Python 2.5. Ini pada dasarnya setara dengansubprocess.run(..., check=True)
subprocess.call()
diperkenalkan dengan Python 2.4 dalamsubprocess
modul asli ( PEP-324 ). Ini pada dasarnya setara dengansubprocess.run(...).returncode
API tingkat tinggi vs
subprocess.Popen()
Refactored dan extended
subprocess.run()
lebih logis dan lebih fleksibel daripada fungsi lawas yang digantikannya. Ini mengembalikanCompletedProcess
objek yang memiliki berbagai metode yang memungkinkan Anda untuk mengambil status keluar, output standar, dan beberapa hasil lainnya dan indikator status dari proses yang selesai.subprocess.run()
adalah cara untuk pergi jika Anda hanya perlu program untuk menjalankan dan mengembalikan kontrol ke Python. Untuk skenario yang lebih terlibat (proses latar belakang, mungkin dengan I / O interaktif dengan program induk Python) Anda masih perlu menggunakansubprocess.Popen()
dan mengurus semua pipa ledeng sendiri. Ini membutuhkan pemahaman yang cukup rumit tentang semua bagian yang bergerak dan tidak boleh dianggap enteng.Popen
Objek yang lebih sederhana mewakili proses (mungkin masih berjalan) yang perlu dikelola dari kode Anda untuk sisa masa hidup dari subproses.Mungkin harus ditekankan bahwa
subprocess.Popen()
sekadar menciptakan proses. Jika Anda membiarkannya di situ, Anda memiliki subproses yang berjalan bersamaan dengan Python, jadi proses "latar belakang". Jika tidak perlu melakukan input atau output atau berkoordinasi dengan Anda, itu dapat melakukan pekerjaan yang berguna secara paralel dengan program Python Anda.Hindari
os.system()
danos.popen()
Sejak waktu abadi (well, sejak Python 2.5)
os
dokumentasi modul telah berisi rekomendasi untuk dipilih lebihsubprocess
darios.system()
:Masalah dengan
system()
itu jelas tergantung pada sistem dan tidak menawarkan cara untuk berinteraksi dengan subproses. Ini hanya berjalan, dengan output standar dan kesalahan standar di luar jangkauan Python. Satu-satunya informasi yang Python terima kembali adalah status keluar dari perintah (nol berarti sukses, meskipun makna nilai-nilai bukan nol juga agak bergantung pada sistem).PEP-324 (yang telah disebutkan di atas) berisi alasan yang lebih terperinci mengapa
os.system
masalah dan bagaimanasubprocess
upaya untuk menyelesaikan masalah tersebut.os.popen()
dulu bahkan lebih kuat tidak dianjurkan :Namun, sejak suatu saat di Python 3, telah diterapkan kembali hanya dengan menggunakan
subprocess
, dan mengarahkan kembali kesubprocess.Popen()
dokumentasi untuk detail.Pahami dan biasanya gunakan
check=True
Anda juga akan melihat bahwa
subprocess.call()
ada banyak keterbatasan yang sama denganos.system()
. Dalam penggunaan reguler, Anda biasanya harus memeriksa apakah proses selesai dengan sukses, manasubprocess.check_call()
dansubprocess.check_output()
apakah (di mana yang terakhir juga mengembalikan output standar dari subproses yang selesai). Demikian pula, Anda harus biasanya menggunakancheck=True
dengansubprocess.run()
kecuali Anda secara khusus perlu untuk memungkinkan subproses untuk mengembalikan status kesalahan.Dalam praktiknya, dengan
check=True
atausubprocess.check_*
, Python akan mengeluarkanCalledProcessError
pengecualian jika subproses mengembalikan status keluar yang bukan nol.Kesalahan umum
subprocess.run()
adalah dengan menghilangkancheck=True
dan terkejut ketika kode hilir gagal jika subproses gagal.Di sisi lain, masalah umum dengan
check_call()
dancheck_output()
adalah bahwa pengguna yang secara buta menggunakan fungsi-fungsi ini terkejut ketika pengecualian muncul misalnya ketikagrep
tidak menemukan kecocokan. (Anda mungkin harus menggantigrep
dengan kode Python asli, seperti diuraikan di bawah ini.)Semua hal diperhitungkan, Anda perlu memahami bagaimana perintah shell mengembalikan kode keluar, dan dalam kondisi apa mereka akan mengembalikan kode keluar non-nol (kesalahan), dan membuat keputusan sadar bagaimana tepatnya harus ditangani.
Memahami dan mungkin menggunakan
text=True
aliasuniversal_newlines=True
Sejak Python 3, string internal ke Python adalah string Unicode. Tetapi tidak ada jaminan bahwa suatu proses menghasilkan keluaran Unicode, atau string sama sekali.
(Jika perbedaannya tidak segera jelas, Unicode Pragmatis Ned Batchelder direkomendasikan, jika tidak secara langsung wajib, membaca. Ada presentasi video 36 menit di belakang tautan jika Anda mau, meskipun membaca halaman sendiri mungkin akan memakan waktu lebih sedikit. )
Jauh di lubuk hati, Python harus mengambil
bytes
buffer dan menafsirkannya entah bagaimana. Jika itu berisi gumpalan data biner, itu tidak boleh diterjemahkan ke dalam string Unicode, karena itu perilaku yang rawan kesalahan dan bug - tepatnya jenis perilaku sial yang membingungkan banyak skrip Python 2, sebelum ada cara untuk benar membedakan antara teks yang disandikan dan data biner.Dengan
text=True
, Anda memberi tahu Python bahwa Anda, pada kenyataannya, mengharapkan kembali data tekstual dalam pengkodean default sistem, dan bahwa itu harus diterjemahkan ke dalam string Python (Unicode) ke yang terbaik dari kemampuan Python (biasanya UTF-8 pada tingkat sedang hingga sistem tanggal, kecuali mungkin Windows?)Jika bukan itu yang Anda minta kembali, Python hanya akan memberi Anda
bytes
string distdout
danstderr
string. Mungkin di beberapa kemudian mengarahkan Anda lakukan tahu bahwa mereka string teks setelah semua, dan kau tahu pengkodean mereka. Kemudian, Anda dapat memecahkan kode mereka.Python 3.7 memperkenalkan alias yang lebih pendek dan lebih deskriptif dan dapat dipahami
text
untuk argumen kata kunci yang sebelumnya agak menyesatkanuniversal_newlines
.Pahami
shell=True
vsshell=False
Dengan
shell=True
Anda memberikan satu string ke shell Anda, dan shell mengambilnya dari sana.Dengan
shell=False
Anda melewati daftar argumen ke OS, melewati shell.Ketika Anda tidak memiliki shell, Anda menyimpan sebuah proses dan menyingkirkan sejumlah kompleksitas tersembunyi yang cukup besar, yang mungkin atau mungkin tidak mengandung bug atau bahkan masalah keamanan.
Di sisi lain, ketika Anda tidak memiliki shell, Anda tidak memiliki redirection, ekspansi wildcard, kontrol pekerjaan, dan sejumlah besar fitur shell lainnya.
Kesalahan umum adalah menggunakan
shell=True
dan kemudian masih memberikan Python daftar token, atau sebaliknya. Ini terjadi pada beberapa kasus, tetapi benar-benar tidak jelas dan bisa pecah dengan cara yang menarik.Retort yang umum "tapi itu bekerja untuk saya" bukanlah sanggahan yang berguna kecuali jika Anda mengerti persis dalam keadaan apa itu bisa berhenti bekerja.
Contoh Refactoring
Sangat sering, fitur shell dapat diganti dengan kode Python asli. Awk sederhana atau
sed
skrip sederhana mungkin seharusnya hanya diterjemahkan ke Python saja.Untuk mengilustrasikannya secara parsial, berikut adalah contoh tipikal tetapi sedikit konyol yang melibatkan banyak fitur shell.
Beberapa hal yang perlu diperhatikan di sini:
shell=False
Anda tidak perlu mengutip bahwa shell membutuhkan sekitar string. Menempatkan tanda kutip mungkin kesalahan.Kode refactored juga menggambarkan seberapa banyak shell benar-benar bekerja untuk Anda dengan sintaks yang sangat singkat - baik atau buruk. Python mengatakan eksplisit adalah lebih baik daripada implisit tetapi kode python adalah agak verbose dan bisa dibilang terlihat lebih kompleks daripada ini benar-benar. Di sisi lain, ia menawarkan sejumlah poin di mana Anda dapat mengambil kendali di tengah-tengah sesuatu yang lain, sebagaimana dicontohkan oleh peningkatan yang kita dapat dengan mudah memasukkan nama host bersama dengan output perintah shell. (Ini sama sekali tidak menantang untuk dilakukan di shell, tetapi dengan mengorbankan pengalihan lain dan mungkin proses lain.)
Konstruksi Shell Umum
Untuk kelengkapan, berikut adalah penjelasan singkat tentang beberapa fitur shell ini, dan beberapa catatan tentang bagaimana mereka dapat diganti dengan fasilitas Python asli.
glob.glob()
atau sangat sering dengan perbandingan string Python sederhana sepertifor file in os.listdir('.'): if not file.endswith('.png'): continue
. Bash memiliki berbagai fasilitas ekspansi lain seperti.{png,jpg}
ekspansi brace dan{1..100}
juga ekspansi tilde (~
memperluas ke direktori home Anda, dan lebih umum~account
ke direktori home dari pengguna lain)$SHELL
atau$my_exported_var
kadang-kadang bisa dengan mudah diganti dengan variabel Python. Variabel shell yang diekspor tersedia sebagai misalnyaos.environ['SHELL']
(artinyaexport
adalah membuat variabel tersedia untuk subproses - variabel yang tidak tersedia untuk subproses jelas tidak akan tersedia untuk Python berjalan sebagai subproses shell, atau sebaliknya. Kataenv=
kunci argumen kesubprocess
metode memungkinkan Anda untuk mendefinisikan lingkungan subproses sebagai kamus, jadi itu salah satu cara untuk membuat variabel Python terlihat oleh sebuah subproses). Denganshell=False
Anda akan perlu memahami cara menghapus tanda kutip; misalnya,cd "$HOME"
setara denganos.chdir(os.environ['HOME'])
tanpa tanda kutip di sekitar nama direktori. (Sangat seringcd
tidak berguna atau tidak diperlukan, dan banyak pemula menghilangkan tanda kutip ganda di sekitar variabel dan lolos sampai suatu hari ... )grep 'foo' <inputfile >outputfile
terbukaoutputfile
untuk menulis daninputfile
membaca, dan meneruskan isinya sebagai input standargrep
, yang kemudian menjadi standar keluaranoutputfile
. Ini biasanya tidak sulit untuk diganti dengan kode Python asli.echo foo | nl
menjalankan dua subproses, di mana output standarecho
adalah input standarnl
(pada tingkat OS, dalam sistem mirip Unix, ini adalah satu pegangan file). Jika Anda tidak dapat mengganti satu atau kedua ujung pipa dengan kode Python asli, mungkin berpikir tentang menggunakan shell setelah semua, terutama jika pipa memiliki lebih dari dua atau tiga proses (meskipun melihatpipes
modul di pustaka standar Python atau angka pesaing pihak ketiga yang lebih modern dan serbaguna).ls -l /
sama dengan'ls' '-l' '/'
tetapi mengutip sekitar literal sepenuhnya opsional. String yang tidak dikutip yang mengandung karakter meta shell menjalani ekspansi parameter, tokenization whitespace, dan ekspansi wildcard; Kutipan ganda mencegah tokenization whitespace dan ekspansi wildcard tetapi memungkinkan ekspansi parameter (substitusi variabel, substitusi perintah, dan pemrosesan backslash). Ini sederhana dalam teori tetapi bisa membingungkan, terutama ketika ada beberapa lapisan interpretasi (perintah shell jarak jauh, misalnya).Memahami perbedaan antara
sh
dan Bashsubprocess
menjalankan perintah shell/bin/sh
Anda kecuali Anda secara khusus meminta sebaliknya (kecuali tentu saja pada Windows, di mana ia menggunakan nilaiCOMSPEC
variabel). Ini berarti bahwa berbagai fitur Bash-only seperti array,[[
dll tidak tersedia.Jika Anda perlu menggunakan sintaks Bash-only, Anda dapat meneruskan jalur ke shell sebagai
executable='/bin/bash'
(di mana tentu saja jika Bash Anda diinstal di tempat lain, Anda perlu menyesuaikan jalur).A
subprocess
terpisah dari induknya, dan tidak dapat mengubahnyaKesalahan yang agak umum adalah melakukan sesuatu seperti
yang terlepas dari kurangnya keanggunan juga mengkhianati kurangnya pemahaman mendasar tentang "sub" bagian dari nama "subproses".
Proses anak berjalan sepenuhnya terpisah dari Python, dan ketika selesai, Python tidak tahu apa yang dilakukannya (terlepas dari indikator samar yang dapat disimpulkan dari status keluar dan output dari proses anak). Seorang anak umumnya tidak dapat mengubah lingkungan orang tua; itu tidak dapat mengatur variabel, mengubah direktori kerja, atau, dalam banyak kata, berkomunikasi dengan orang tuanya tanpa kerja sama dari orang tua.
Perbaikan langsung dalam kasus khusus ini adalah menjalankan kedua perintah dalam satu proses tunggal;
meskipun jelas ini kasus penggunaan khusus tidak memerlukan shell sama sekali. Ingat, Anda dapat memanipulasi lingkungan proses saat ini (dan juga anak-anaknya) melalui
atau melewati pengaturan lingkungan ke proses anak dengan
(belum lagi refactoring yang jelas
subprocess.run(['echo', 'bar'])
; tetapiecho
merupakan contoh yang buruk dari sesuatu untuk dijalankan di subproses di tempat pertama, tentu saja).Jangan jalankan Python dari Python
Ini sedikit nasihat yang meragukan; pasti ada situasi di mana itu masuk akal atau bahkan merupakan persyaratan mutlak untuk menjalankan interpreter Python sebagai subproses dari skrip Python. Tetapi sangat sering, pendekatan yang benar adalah hanya dengan
import
modul Python lain ke dalam skrip panggilan Anda dan memanggil fungsinya secara langsung.Jika skrip Python lain berada di bawah kendali Anda, dan itu bukan modul, pertimbangkan untuk mengubahnya menjadi satu . (Jawaban ini sudah terlalu lama jadi saya tidak akan membahas detailnya di sini.)
Jika Anda membutuhkan paralelisme, Anda dapat menjalankan fungsi Python di subproses dengan
multiprocessing
modul. Ada jugathreading
yang menjalankan banyak tugas dalam satu proses (yang lebih ringan dan memberi Anda lebih banyak kontrol, tetapi juga lebih dibatasi bahwa utas dalam suatu proses digabungkan secara ketat, dan terikat pada satu GIL .)sumber
run()
secara membabi buta. Hilangcheck=True
menyebabkan bug yang akan dihindari jika check_call digunakan - "check" ada dalam nama, Anda tidak bisa menghilangkannya — ini adalah default yang benar: jangan abaikan kesalahan secara diam-diam. Saya tidak membaca lebih lanjut.sh
tetapi Anda mengalahkan saya untuk itu. Saya mencoba untuk menjabarkan secara terperinci untuk membantu pemula yang jebakannya tidak jelas sehingga tidak terlalu lama. Anda harus cukup memadai jika tidak; +1stderr/stdout = subprocess.PIPE
memiliki overhead kinerja yang lebih tinggi daripada pengaturan default?os.system()
.Sebut saja dengan subproses
Kesalahan yang Anda dapatkan tampaknya karena tidak ada modul swap di server, Anda harus menginstal swap di server kemudian jalankan skrip lagi
sumber
swap
Modul jelas ada, karena menjalankan perintah dari karya-karya shell.shell=True
maka Anda harus menggunakan daftar untuk melewati beberapa argumen yaitu, gunakan['a', 'b', 'c']
bukan'a b c'
. Meskipun split naif tidak akan berfungsi karena> file
(pengalihan shell) dalam perintah. Lebih detailMungkin Anda menggunakan program bash, dengan parameter -c untuk menjalankan perintah:
sumber
subprocess.check_output(bashCommand, shell=True)
melakukan hal yang sama. Jika perintah Anda adalah string statis, coba parsing sendiri ke daftar dan hindarishell=True
; meskipun dalam kasus ini Anda tetap memerlukan shell untuk redirection, atau Anda perlu refactor ke Python murni -with open('test.nt', 'w') as dest: output = subprocess.check_output(['cwm' ,'--rdf', 'test.rdf', '--ntriples'], stdout=dest, shell=False)
/bin/sh
(digunakan oleh subprocess) belum tentubash
(Anda tidak dapat menggunakan bashisms). Padahal orang bisa menggunakanexecutable='/bin/bash
jika diinginkan. Berikut ini contoh kodecheck_output()
tidak berguna di sini (output selalu kosong karena> file
pengalihan; gunakancheck_call()
sebagai gantinya.Anda dapat menggunakan
subprocess
, tetapi saya selalu merasa bahwa itu bukan cara 'Pythonic' untuk melakukannya. Jadi saya membuat Sultan (plug shameless) yang membuatnya mudah untuk menjalankan fungsi command line.https://github.com/aeroxis/sultan
sumber
Menurut kesalahan Anda kehilangan paket bernama swap di server. Ini
/usr/bin/cwm
membutuhkannya. Jika Anda menggunakan Ubuntu / Debian, instalpython-swap
menggunakan aptitude.sumber
swap
atau seharusnya tidak mengimpornya di tempat pertama. bisakah kamuimport swap
secara manual? Apakah itu bekerja?sys.path
mana itu berfungsi dan di mana tidak. Kemudian coba cari folder swap atau swap.py di folder yang dicetak. Seperti yang dikatakan Sven, mungkin ada masalah dengan jalur itu, dan ini akan membantu Anda mengetahuinya.Anda juga dapat menggunakan 'os.popen'. Contoh:
Keluaran:
sumber
subprocess
modul."os.popen
tidak ada lagi peringatan ini, dan hanya bungkus tipis di sekitarsubprocess.Popen()
sekarang.Untuk menjalankan perintah tanpa shell, meneruskan perintah sebagai daftar dan mengimplementasikan pengalihan dengan Python menggunakan
[subprocess]
:Catatan: tidak
> test.nt
di bagian akhir.stdout=file
mengimplementasikan pengalihan.Untuk menjalankan perintah menggunakan shell di Python, kirimkan perintah sebagai string dan aktifkan
shell=True
:Inilah shell yang bertanggung jawab untuk redirection output (
> test.nt
ada di perintah).Untuk menjalankan perintah bash yang menggunakan bashism, tentukan bash yang dapat dieksekusi secara eksplisit misalnya, untuk meniru substitusi proses bash :
sumber
.split()
tidak memadai ketika ada kutipan string dll. Ada rutin terpisahshlex.split()
yang berupaya dengan sintaksis kompleks shell sewenang-wenang..split()
karya dalam hal ini.shlex.split()
kadang-kadang bisa bermanfaat tetapi mungkin gagal dalam beberapa kasus juga. Ada banyak hal hebat yang dapat disebutkan. Anda bisa mulai dengan tautan ke deskripsi tag subproses yang disediakan di atas.Cara pythonic melakukan ini adalah menggunakan
subprocess.Popen
subprocess.Popen
mengambil daftar di mana elemen pertama adalah perintah yang akan dijalankan diikuti oleh argumen baris perintah apa pun.Sebagai contoh:
sumber
echo -v '"Hello Again!"'
dengan tanda kutip tunggal di sekitar tanda kutip ganda.subprocesss.Popen
, Anda harus mengelola objek proses yang dihasilkan (minimal, melakukan await()
untuk mencegahnya berubah menjadi proses zombie).