Menjalankan perintah Bash dengan Python

299

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 bashCommandyang 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?

mkn
sumber
2
Tampaknya ada perbedaan dalam lingkungan tergantung pada bagaimana Anda menjalankannya cwm. Mungkin Anda memiliki beberapa konfigurasi di lingkungan Anda .bashrcyang mengatur lingkungan untuk penggunaan bash interaktif?
Sven Marnach
Apakah Anda mencoba menjalankan perintah dari baris perintah ketika login di server? Posting Anda hanya mengatakan Anda "menempelkannya ke terminal".
Sven Marnach
@Ven: ya saya maksudkan bahwa saya menjalankan perintah langsung di terminal server
mkn
Tampaknya ada perbedaan dalam PYTHONPATH tergantung pada bagaimana Anda menjalankan cwm. Atau mungkin ada perbedaan dalam PATH, dan versi yang berbeda cwmdisebut. Atau versi Python yang berbeda. Sangat sulit untuk mengetahui hal ini tanpa akses ke mesin ...
Sven Marnach

Jawaban:

314

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:

bashCommand = "cwm --rdf test.rdf --ntriples > test.nt"
import subprocess
process = subprocess.Popen(bashCommand.split(), stdout=subprocess.PIPE)
output, error = process.communicate()
pengguna225312
sumber
8
Ini tidak melakukan apa yang saya inginkan ketika saya perlu melakukan cd 'path\to\somewhere'diikuti oleh perintah bash lain yang perlu dijalankan di suatu tempat. @ user225312
AWrightIV
36
@AWrightIV Jika Anda perlu menjalankan subproses di direktori kerja tertentu, Anda dapat menggunakan cwdargumen untuk Popen:subprocess.Popen(..., cwd='path\to\somewhere')
waterproof
7
Untuk perintah saya, saya perlu shell = Benar seperti di sini; stackoverflow.com/questions/18962785/…
user984003
4
Lebih baik menggunakan shlex.split () daripada string.split () dalam kasus ini
Alexey Sviridov
4
... ( stdout=filemengalihkan 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)
jfs
186

Untuk sedikit memperluas jawaban sebelumnya di sini, ada sejumlah detail yang biasanya diabaikan.

  • Lebih subprocess.run()lebih subprocess.check_call()dan teman-teman selama subprocess.call()lebih subprocess.Popen()lebih os.system()lebihos.popen()
  • Pahami dan mungkin gunakan text=True, alias universal_newlines=True.
  • Memahami arti shell=Trueatau shell=Falsedan bagaimana hal itu mengubah penawaran dan ketersediaan kenyamanan shell.
  • Memahami perbedaan antara shdan Bash
  • Memahami bagaimana suatu subproses terpisah dari induknya, dan umumnya tidak dapat mengubah induknya.
  • Hindari menjalankan interpreter Python sebagai subproses dari Python.

Topik-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 :

Pendekatan yang disarankan untuk menjalankan subproses adalah menggunakan run()fungsi untuk semua kasus penggunaan yang dapat ditangani. Untuk kasus penggunaan yang lebih lanjut, Popenantarmuka yang mendasarinya dapat digunakan secara langsung.

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 dalam subprocessmodul 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 mengembalikan CompletedProcessobjek 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 menggunakan subprocess.Popen()dan mengurus semua pipa ledeng sendiri. Ini membutuhkan pemahaman yang cukup rumit tentang semua bagian yang bergerak dan tidak boleh dianggap enteng. PopenObjek 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) osdokumentasi modul telah berisi rekomendasi untuk dipilih lebih subprocessdari os.system():

The subprocessModul menyediakan fasilitas yang lebih kuat untuk pemijahan proses baru dan mengambil hasil mereka; menggunakan modul itu lebih baik daripada menggunakan fungsi ini.

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.systemmasalah dan bagaimana subprocessupaya untuk menyelesaikan masalah tersebut.

os.popen()dulu bahkan lebih kuat tidak dianjurkan :

Tidak digunakan lagi sejak versi 2.6: Fungsi ini sudah usang. Gunakan subprocessmodul.

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 dengan os.system(). Dalam penggunaan reguler, Anda biasanya harus memeriksa apakah proses selesai dengan sukses, mana subprocess.check_call()dan subprocess.check_output()apakah (di mana yang terakhir juga mengembalikan output standar dari subproses yang selesai). Demikian pula, Anda harus biasanya menggunakan check=Truedengan subprocess.run()kecuali Anda secara khusus perlu untuk memungkinkan subproses untuk mengembalikan status kesalahan.

Dalam praktiknya, dengan check=Trueatau subprocess.check_*, Python akan mengeluarkan CalledProcessErrorpengecualian 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()dan check_output()adalah bahwa pengguna yang secara buta menggunakan fungsi-fungsi ini terkejut ketika pengecualian muncul misalnya ketika greptidak menemukan kecocokan. (Anda mungkin harus mengganti grepdengan 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=Truealiasuniversal_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 bytesbuffer 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 bytesstring di stdoutdan stderrstring. 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.

normal = subprocess.run([external, arg],
    stdout=subprocess.PIPE, stderr=subprocess.PIPE,
    check=True,
    text=True)
print(normal.stdout)

convoluted = subprocess.run([external, arg],
    stdout=subprocess.PIPE, stderr=subprocess.PIPE,
    check=True)
# You have to know (or guess) the encoding
print(convoluted.stdout.decode('utf-8'))

Python 3.7 memperkenalkan alias yang lebih pendek dan lebih deskriptif dan dapat dipahami textuntuk argumen kata kunci yang sebelumnya agak menyesatkan universal_newlines.

Pahami shell=Truevsshell=False

Dengan shell=TrueAnda memberikan satu string ke shell Anda, dan shell mengambilnya dari sana.

Dengan shell=FalseAnda 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=Truedan 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.

# XXX AVOID THIS BUG
buggy = subprocess.run('dig +short stackoverflow.com')

# XXX AVOID THIS BUG TOO
broken = subprocess.run(['dig', '+short', 'stackoverflow.com'],
    shell=True)

# XXX DEFINITELY AVOID THIS
pathological = subprocess.run(['dig +short stackoverflow.com'],
    shell=True)

correct = subprocess.run(['dig', '+short', 'stackoverflow.com'],
    # Probably don't forget these, too
    check=True, text=True)

# XXX Probably better avoid shell=True
# but this is nominally correct
fixed_but_fugly = subprocess.run('dig +short stackoverflow.com',
    shell=True,
    # Probably don't forget these, too
    check=True, text=True)

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 ataused 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.

cmd = '''while read -r x;
   do ping -c 3 "$x" | grep 'round-trip min/avg/max'
   done <hosts.txt'''

# Trivial but horrible
results = subprocess.run(
    cmd, shell=True, universal_newlines=True, check=True)
print(results.stdout)

# Reimplement with shell=False
with open('hosts.txt') as hosts:
    for host in hosts:
        host = host.rstrip('\n')  # drop newline
        ping = subprocess.run(
             ['ping', '-c', '3', host],
             text=True,
             stdout=subprocess.PIPE,
             check=True)
        for line in ping.stdout.split('\n'):
             if 'round-trip min/avg/max' in line:
                 print('{}: {}'.format(host, line))

Beberapa hal yang perlu diperhatikan di sini:

  • Dengan shell=FalseAnda tidak perlu mengutip bahwa shell membutuhkan sekitar string. Menempatkan tanda kutip mungkin kesalahan.
  • Seringkali masuk akal untuk menjalankan kode sesedikit mungkin dalam suatu subproses. Ini memberi Anda lebih banyak kontrol atas eksekusi dari dalam kode Python Anda.
  • Karena itu, jaringan pipa shell yang rumit membosankan dan kadang-kadang menantang untuk diterapkan kembali dengan Python.

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.

  • Globbing alias ekspansi wildcard dapat diganti dengan glob.glob()atau sangat sering dengan perbandingan string Python sederhana seperti for 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 ~accountke direktori home dari pengguna lain)
  • Variabel Shell suka $SHELLatau $my_exported_varkadang-kadang bisa dengan mudah diganti dengan variabel Python. Variabel shell yang diekspor tersedia sebagai misalnya os.environ['SHELL'](artinya exportadalah membuat variabel tersedia untuk subproses - variabel yang tidak tersedia untuk subproses jelas tidak akan tersedia untuk Python berjalan sebagai subproses shell, atau sebaliknya. Kata env=kunci argumen ke subprocessmetode memungkinkan Anda untuk mendefinisikan lingkungan subproses sebagai kamus, jadi itu salah satu cara untuk membuat variabel Python terlihat oleh sebuah subproses). Dengan shell=FalseAnda akan perlu memahami cara menghapus tanda kutip; misalnya, cd "$HOME"setara denganos.chdir(os.environ['HOME']) tanpa tanda kutip di sekitar nama direktori. (Sangat seringcdtidak berguna atau tidak diperlukan, dan banyak pemula menghilangkan tanda kutip ganda di sekitar variabel dan lolos sampai suatu hari ... )
  • Pengalihan memungkinkan Anda untuk membaca dari file sebagai input standar Anda, dan menulis output standar Anda ke file. grep 'foo' <inputfile >outputfileterbuka outputfileuntuk menulis dan inputfilemembaca, dan meneruskan isinya sebagai input standar grep, yang kemudian menjadi standar keluaran outputfile. Ini biasanya tidak sulit untuk diganti dengan kode Python asli.
  • Pipa adalah bentuk pengalihan. echo foo | nlmenjalankan dua subproses, di mana output standar echoadalah input standar nl(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 melihat pipesmodul di pustaka standar Python atau angka pesaing pihak ketiga yang lebih modern dan serbaguna).
  • Kontrol pekerjaan memungkinkan Anda menyela pekerjaan, menjalankannya di latar belakang, mengembalikannya ke latar depan, dll. Sinyal Unix dasar untuk berhenti dan melanjutkan proses tentu saja juga tersedia dari Python. Tetapi pekerjaan adalah abstraksi level yang lebih tinggi dalam shell yang melibatkan kelompok proses dll yang harus Anda pahami jika Anda ingin melakukan sesuatu seperti ini dari Python.
  • Mengutip di shell berpotensi membingungkan sampai Anda memahami bahwa semuanya pada dasarnya adalah sebuah string. Jadi 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 shdan Bash

subprocessmenjalankan perintah shell /bin/shAnda kecuali Anda secara khusus meminta sebaliknya (kecuali tentu saja pada Windows, di mana ia menggunakan nilai COMSPECvariabel). 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).

subprocess.run('''
    # This for loop syntax is Bash only
    for((i=1;i<=$#;i++)); do
        # Arrays are Bash-only
        array[i]+=123
    done''',
    shell=True, check=True,
    executable='/bin/bash')

A subprocessterpisah dari induknya, dan tidak dapat mengubahnya

Kesalahan yang agak umum adalah melakukan sesuatu seperti

subprocess.run('foo=bar', shell=True)
subprocess.run('echo "$foo"', shell=True)  # Doesn't work

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;

subprocess.run('foo=bar; echo "$foo"', shell=True)

meskipun jelas ini kasus penggunaan khusus tidak memerlukan shell sama sekali. Ingat, Anda dapat memanipulasi lingkungan proses saat ini (dan juga anak-anaknya) melalui

os.environ['foo'] = 'bar'

atau melewati pengaturan lingkungan ke proses anak dengan

subprocess.run('echo "$foo"', shell=True, env={'foo': 'bar'})

(belum lagi refactoring yang jelas subprocess.run(['echo', 'bar']); tetapi echomerupakan 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 importmodul 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 multiprocessingmodul. Ada juga threadingyang 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 .)

tripleee
sumber
2
Untuk penjelasan lebih rinci tentang bagaimana Anda bisa menghindari memanggil Python sebagai subproses, lihat jawaban ini pada pertanyaan yang hampir serupa.
tripleee
4
itu mengejutkan saya bahwa saya harus memposting jawaban baru untuk pertanyaan dasar seperti itu untuk menunjukkan bagaimana menjalankan perintah dari pertanyaan secara idiomatis. Jawaban Anda panjang tapi saya tidak melihat contoh seperti itu. Tidak terkait: hindari pemujaan kargo. Jika check_call () berfungsi dalam kasus Anda, gunakan itu. Saya harus memperbaiki kode yang digunakan run()secara membabi buta. Hilang check=Truemenyebabkan 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.
jfs
1
@ jfs Terima kasih atas umpan baliknya, saya sebenarnya berencana untuk menambahkan bagian tentang Bash vs shtetapi 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; +1
tripleee
Apakah stderr/stdout = subprocess.PIPEmemiliki overhead kinerja yang lebih tinggi daripada pengaturan default?
Stringer
1
@ Stringers Saya belum menguji, tapi saya tidak melihat mengapa itu harus. Jika Anda menghubungkan pipa-pipa itu dengan sesuatu yang melakukan pemrosesan, maka tentu saja pemrosesan itu perlu dilakukan; tetapi itu tidak terjadi pada pipa itu sendiri. Standarnya adalah untuk tidak menangkap stdout atau stderr sama sekali, yaitu apa pun yang dicetak ada di luar visibilitas dan kontrol Python, seperti halnya dengan os.system().
tripleee
41

Sebut saja dengan subproses

import subprocess
subprocess.Popen("cwm --rdf test.rdf --ntriples > test.nt")

Kesalahan yang Anda dapatkan tampaknya karena tidak ada modul swap di server, Anda harus menginstal swap di server kemudian jalankan skrip lagi

Jakob Bowyer
sumber
3
The swapModul jelas ada, karena menjalankan perintah dari karya-karya shell.
Sven Marnach
2
Bukan di server, ketika ia menjalankannya di server ada kesalahan impor.
Jakob Bowyer
@ mkn: "Lalu saya baru saja menyalin output itu dan melakukan copy paste ke terminal dan tekan enter dan berfungsi ..." - Apakah Anda mencoba ini di server atau di mesin Anda?
Sven Marnach
Apakah Anda menjalankan ini pada komputer yang berdiri sendiri baik-baik saja tetapi tidak berfungsi ketika Anda menjalankannya di server Anda? Atau apakah Anda dapat menjalankannya pada terminal server tetapi tidak pada server itu sendiri
Jakob Bowyer
1
itu salah. Jika Anda tidak menggunakan shell=Truemaka 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 detail
jfs
18

Mungkin Anda menggunakan program bash, dengan parameter -c untuk menjalankan perintah:

bashCommand = "cwm --rdf test.rdf --ntriples > test.nt"
output = subprocess.check_output(['bash','-c', bashCommand])
Pisau cukur
sumber
2
subprocess.check_output(bashCommand, shell=True)melakukan hal yang sama. Jika perintah Anda adalah string statis, coba parsing sendiri ke daftar dan hindari shell=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)
tripleee
@ tripleee note: /bin/sh(digunakan oleh subprocess) belum tentu bash(Anda tidak dapat menggunakan bashisms). Padahal orang bisa menggunakan executable='/bin/bashjika diinginkan. Berikut ini contoh kode
jfs
2
itu adalah jawaban pertama di mana perintah harus mulai berhasil (jawaban populer yang diterima dan ke-2 hanya salah. Sebuah berdalih kecil: check_output()tidak berguna di sini (output selalu kosong karena > filepengalihan; gunakan check_call()sebagai gantinya.
jfs
16

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

David Daniel
sumber
3
Sudah selesai dilakukan dengan baik! Jauh lebih bersih dan lebih intuitif daripada subproses.
mjd2
Terima kasih banyak! Saya senang mendengar itu!
David Daniel
2
Ini harus secara jujur ​​diadopsi ke dalam perpustakaan standar.
Joshua Detwiler
1
Apakah ada cara untuk menangkap output dari terminal menggunakan Sultan?
alvas
Ya, Anda dapat @alvas ... Ini dokumen tentang cara melakukannya: sultan.readthedocs.io/en/latest/…
David Daniel
7

Menurut kesalahan Anda kehilangan paket bernama swap di server. Ini /usr/bin/cwmmembutuhkannya. Jika Anda menggunakan Ubuntu / Debian, instal python-swapmenggunakan aptitude.

kichik
sumber
tetapi berfungsi ketika saya menjalankannya langsung di terminal ... jadi swap harus ada di sana, bukan?
mkn
ada dua opsi. baik itu tidak dapat menemukan swapatau seharusnya tidak mengimpornya di tempat pertama. bisakah kamu import swapsecara manual? Apakah itu bekerja?
kichik
hm saya tidak bisa. Jika saya memulai python dengan mengetikkan python di terminal dan kemudian saya mengetik import swap maka saya mendapat kesalahan "ImportError: No module bernama swap". Yang aneh adalah masih berfungsi ketika saya menjalankan perintah cwm langsung di terminal server
mkn
Coba cetak di sys.pathmana 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.
kichik
4

Anda juga dapat menggunakan 'os.popen'. Contoh:

import os

command = os.popen('ls -al')
print(command.read())
print(command.close())

Keluaran:

total 16
drwxr-xr-x 2 root root 4096 ago 13 21:53 .
drwxr-xr-x 4 root root 4096 ago 13 01:50 ..
-rw-r--r-- 1 root root 1278 ago 13 21:12 bot.py
-rw-r--r-- 1 root root   77 ago 13 21:53 test.py

None
ricardo130
sumber
1
Dokumentasi berisi kotak merah besar: " Tidak digunakan sejak versi 2.6: Fungsi ini sudah usang. Gunakan subprocessmodul."
tripleee
1
Dalam keadilan, os.popentidak ada lagi peringatan ini, dan hanya bungkus tipis di sekitar subprocess.Popen()sekarang.
tripleee
4

Untuk menjalankan perintah tanpa shell, meneruskan perintah sebagai daftar dan mengimplementasikan pengalihan dengan Python menggunakan [subprocess]:

#!/usr/bin/env python
import subprocess

with open('test.nt', 'wb', 0) as file:
    subprocess.check_call("cwm --rdf test.rdf --ntriples".split(),
                          stdout=file)

Catatan: tidak > test.ntdi bagian akhir. stdout=filemengimplementasikan pengalihan.


Untuk menjalankan perintah menggunakan shell di Python, kirimkan perintah sebagai string dan aktifkan shell=True:

#!/usr/bin/env python
import subprocess

subprocess.check_call("cwm --rdf test.rdf --ntriples > test.nt",
                      shell=True)

Inilah shell yang bertanggung jawab untuk redirection output ( > test.ntada di perintah).


Untuk menjalankan perintah bash yang menggunakan bashism, tentukan bash yang dapat dieksekusi secara eksplisit misalnya, untuk meniru substitusi proses bash :

#!/usr/bin/env python
import subprocess

subprocess.check_call('program <(command) <(another-command)',
                      shell=True, executable='/bin/bash')
jfs
sumber
Mungkin menyebutkan bahwa .split()tidak memadai ketika ada kutipan string dll. Ada rutin terpisah shlex.split()yang berupaya dengan sintaksis kompleks shell sewenang-wenang.
tripleee
@ Tripleee .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.
jfs
0

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:

import subprocess

args = ['echo', 'Hello!']
subprocess.Popen(args) // same as running `echo Hello!` on cmd line

args2 = ['echo', '-v', '"Hello Again"']
subprocess.Popen(args2) // same as running 'echo -v "Hello Again!"` on cmd line
Jawaban yang Dijawab Ulang
sumber
Tidak, contoh terakhir sama dengan menjalankan echo -v '"Hello Again!"'dengan tanda kutip tunggal di sekitar tanda kutip ganda.
tripleee
Juga, untuk menggunakan dengan benar subprocesss.Popen, Anda harus mengelola objek proses yang dihasilkan (minimal, melakukan a wait()untuk mencegahnya berubah menjadi proses zombie).
tripleee