Mengapa menggunakan metode modul os Python daripada menjalankan perintah shell secara langsung?

157

Saya mencoba memahami apa motivasi di balik penggunaan fungsi pustaka Python untuk menjalankan tugas-tugas spesifik OS seperti membuat file / direktori, mengubah atribut file, dll. Daripada hanya menjalankan perintah-perintah itu melalui os.system()atau subprocess.call()?

Misalnya, mengapa saya ingin menggunakan os.chmoddaripada melakukan os.system("chmod...")?

Saya mengerti bahwa lebih "pythonic" untuk menggunakan metode pustaka Python yang tersedia sebanyak mungkin daripada hanya menjalankan perintah shell secara langsung. Tetapi, adakah motivasi lain di balik melakukan hal ini dari sudut pandang fungsionalitas?

Saya hanya berbicara tentang mengeksekusi perintah shell satu baris sederhana di sini. Ketika kita membutuhkan lebih banyak kontrol atas pelaksanaan tugas, saya mengerti bahwa menggunakan subprocessmodul lebih masuk akal, misalnya.

Koderok
sumber
6
Anda pada dasarnya memukul paku di kepala. Tugas tingkat OS yang Anda rujuk cukup umum sehingga mereka menjamin fungsinya sendiri, bukan hanya diturunkan untuk dipanggil melalui sistem os.s.
deweyredman
7
BTW, apakah Anda mencoba waktu waktu eksekusi - os.chmod vs os.system ("chmod ...") . Saya akan menebak bahwa itu akan menjawab bagian dari pertanyaan Anda.
gunung berapi
61
Mengapa printbisa begitu os.system("echo Hello world!")?
user253751
25
Untuk alasan yang sama, Anda harus menggunakan os.pathuntuk menangani jalur alih-alih menanganinya secara manual: ini bekerja pada setiap OS di mana ia berjalan.
Bakuriu
51
"Menjalankan perintah shell secara langsung" sebenarnya kurang langsung. Shell bukan antarmuka tingkat rendah ke sistem, dan os.chmodtidak akan memanggil chmodprogram yang akan dibuat oleh shell. Menggunakan os.system('chmod ...')meluncurkan shell untuk menafsirkan string untuk memanggil lain executable untuk membuat panggilan ke C chmodfungsi, sementara os.chmod(...)berjalan jauh lebih langsung ke C chmod.
user2357112 mendukung Monica

Jawaban:

325
  1. Ini lebih cepat , os.systemdan subprocess.callmembuat proses baru yang tidak perlu untuk sesuatu yang sederhana ini. Bahkan, os.systemdan subprocess.calldengan shellargumen biasanya membuat setidaknya dua proses baru: yang pertama adalah shell, dan yang kedua adalah perintah yang Anda jalankan (jika itu bukan shell built-in like test).

  2. Beberapa perintah tidak berguna dalam proses terpisah . Misalnya, jika Anda menjalankan os.spawn("cd dir/"), itu akan mengubah direktori kerja saat ini dari proses anak, tetapi tidak dari proses Python. Anda perlu menggunakannya os.chdiruntuk itu.

  3. Anda tidak perlu khawatir tentang karakter khusus yang ditafsirkan oleh shell. os.chmod(path, mode)akan bekerja tidak peduli apa nama file itu, sedangkan os.spawn("chmod 777 " + path)akan gagal total jika nama file itu seperti ; rm -rf ~. (Perhatikan bahwa Anda dapat mengatasi ini jika Anda menggunakan subprocess.calltanpa shellargumen.)

  4. Anda tidak perlu khawatir tentang nama file yang dimulai dengan tanda hubung . os.chmod("--quiet", mode)akan mengubah izin file bernama --quiet, tetapi os.spawn("chmod 777 --quiet")akan gagal, sebagaimana --quietditafsirkan sebagai argumen. Ini berlaku bahkan untuk subprocess.call(["chmod", "777", "--quiet"]).

  5. Anda memiliki lebih sedikit masalah lintas-platform dan lintas-shell, karena perpustakaan standar Python seharusnya menangani itu untuk Anda. Apakah sistem Anda memiliki chmodperintah? Apakah sudah diinstal? Apakah itu mendukung parameter yang Anda harapkan didukung? The osmodul akan mencoba untuk menjadi seperti cross-platform mungkin dan dokumen ketika bahwa itu tidak mungkin.

  6. Jika perintah yang Anda jalankan memiliki keluaran yang Anda pedulikan, Anda perlu menguraikannya, yang lebih sulit daripada kedengarannya, karena Anda mungkin melupakan kasus sudut (nama file dengan spasi, tab, dan baris baru di dalamnya), bahkan ketika Anda tidak peduli tentang portabilitas.

Flimm
sumber
38
Untuk menambah titik "lintas platform", daftar direktori adalah "ls" di linux, "dir" di windows. Mendapatkan isi direktori adalah tugas tingkat rendah yang sangat umum.
Cort Ammon
1
@CortAmmon: "Tingkat Rendah" adalah relatif, lsatau dirtingkat yang cukup tinggi untuk jenis pengembang tertentu, sama seperti bashatau cmdatau kshatau apa pun shell yang Anda inginkan.
Sebastian Mach
1
@phirnel: Saya tidak pernah berpikir seperti itu. Bagi saya, "panggilan langsung ke API kernel OS Anda" adalah level yang sangat rendah. Saya berasumsi ada perspektif berbeda tentang hal ini yang menghindarkan saya karena saya (secara alami) mendekatinya dengan bias saya sendiri.
Cort Ammon
5
@CortAmmon: benar, dan lslebih tinggi dari itu, karena ini bukan panggilan langsung ke API kernel OS Anda. Ini adalah aplikasi (kecil).
Steve Jessop
1
@SteveJessop. Saya menelepon "mendapatkan isi direktori" tingkat rendah. Saya tidak berpikir lsatau dirtetapi opendir()/readdir()(linux api) atau FindFirstFile()/FindNextFile()(windows api) atau File.listFiles(java API) atau Directory.GetFiles()(C #). Semua ini terkait erat dengan panggilan langsung ke OS. Beberapa mungkin sesederhana memasukkan nomor ke dalam register dan memanggil int 13huntuk memicu mode kernel.
Cort Ammon
133

Itu lebih aman. Untuk memberi Anda ide di sini adalah contoh skrip

import os
file = raw_input("Please enter a file: ")
os.system("chmod 777 " + file)

Jika input dari pengguna adalah test; rm -rf ~ini maka akan menghapus direktori home.

Inilah sebabnya mengapa lebih aman untuk menggunakan fungsi bawaan.

Karenanya mengapa Anda harus menggunakan subproses alih-alih sistem juga.

iProgram
sumber
26
Atau cara lain untuk melihatnya, apa yang lebih mudah dilakukan, menulis program Python atau menulis program Python yang menulis skrip shell? :-)
Steve Jessop
3
@SteveJessop, seorang rekan saya kagum bahwa skrip Python kecil yang saya bantu dia tulis berhasil 20 (!) Kali lebih cepat skrip tan shell. Saya menjelaskan bahwa redirection output mungkin terlihat seksi - tetapi memerlukan pembukaan dan penutupan file pada setiap iterasi. Tetapi beberapa suka melakukan hal-hal yang sulit - :)
gunung berapi
1
@ SeveJessop, ini pertanyaan jebakan - Anda tidak akan tahu sampai runtime! :)
60

Ada empat kasus kuat untuk memilih metode Python yang lebih spesifik dalam osmodul daripada menggunakan os.systematau subprocessmodul saat menjalankan perintah:

  • Redundansi - menelurkan proses lain adalah redundan dan membuang-buang waktu dan sumber daya.
  • Portabilitas - Banyak metode dalam osmodul ini tersedia di berbagai platform sementara banyak perintah shell bersifat spesifik-os.
  • Memahami hasil - Memunculkan proses untuk mengeksekusi perintah sewenang-wenang memaksa Anda untuk menguraikan hasil dari output dan memahami jika dan mengapa suatu perintah telah melakukan sesuatu yang salah.
  • Keamanan - Suatu proses berpotensi mengeksekusi perintah apa pun yang diberikan. Ini adalah desain yang lemah dan dapat dihindari dengan menggunakan metode spesifik dalam osmodul.

Redundansi (lihat kode redundan ):

Anda sebenarnya mengeksekusi "perantara" yang berlebihan dalam perjalanan Anda ke panggilan sistem yang akhirnya ( chmoddalam contoh Anda). Orang tengah ini adalah proses atau sub-shell baru.

Dari os.system:

Jalankan perintah (string) dalam subkulit ...

Dan subprocesshanya sebuah modul untuk menelurkan proses baru.

Anda dapat melakukan apa yang Anda butuhkan tanpa memunculkan proses ini.

Portabilitas (lihat kode sumber portabilitas ):

Tujuan osmodul adalah untuk menyediakan layanan sistem operasi generik dan uraiannya dimulai dengan:

Modul ini menyediakan cara portabel menggunakan fungsionalitas yang tergantung pada sistem operasi.

Anda dapat menggunakan os.listdirkedua windows dan unix. Mencoba menggunakan os.system/ subprocessuntuk fungsi ini akan memaksa Anda untuk mempertahankan dua panggilan (untuk ls/ dir) dan memeriksa sistem operasi yang Anda gunakan . Ini tidak portabel dan akan menyebabkan frustrasi lebih banyak di kemudian hari (lihat Menangani Penanganan ).

Memahami hasil perintah:

Misalkan Anda ingin membuat daftar file dalam direktori.

Jika Anda menggunakan os.system("ls")/ subprocess.call(['ls']), Anda hanya bisa mendapatkan kembali proses, yang pada dasarnya adalah string besar dengan nama file.

Bagaimana Anda bisa memberi tahu file dengan spasi di namanya dari dua file?

Bagaimana jika Anda tidak memiliki izin untuk mendaftarkan file?

Bagaimana seharusnya Anda memetakan data ke objek python?

Ini hanya di atas kepala saya, dan sementara ada solusi untuk masalah ini - mengapa memecahkan lagi masalah yang dipecahkan untuk Anda?

Ini adalah contoh mengikuti prinsip Don't Repeat Yourself (Sering disebut sebagai "KERING") dengan tidak mengulangi implementasi yang sudah ada dan tersedia secara bebas untuk Anda.

Keamanan:

os.systemdan subprocesssangat kuat. Itu bagus ketika Anda membutuhkan kekuatan ini, tetapi itu berbahaya ketika Anda tidak. Ketika Anda menggunakan os.listdir, Anda tahu itu tidak bisa melakukan hal lain selain daftar file atau meningkatkan kesalahan. Ketika Anda menggunakan os.systematau subprocessuntuk mencapai perilaku yang sama, Anda berpotensi berakhir melakukan sesuatu yang tidak Anda inginkan.

Keselamatan Injeksi (lihat contoh injeksi shell ) :

Jika Anda menggunakan input dari pengguna sebagai perintah baru, pada dasarnya Anda memberinya shell. Ini seperti injeksi SQL yang menyediakan shell di DB untuk pengguna.

Contohnya adalah perintah dari formulir:

# ... read some user input
os.system(user_input + " some continutation")

Ini dapat dengan mudah dieksploitasi untuk menjalankan kode arbitrer apa pun menggunakan input: NASTY COMMAND;#untuk membuat akhirnya:

os.system("NASTY COMMAND; # some continuation")

Ada banyak perintah yang dapat membahayakan sistem Anda.

Reut Sharabani
sumber
3
Saya akan mengatakan 2. adalah alasan utama.
jaredad7
23

Untuk alasan sederhana - ketika Anda memanggil fungsi shell, itu membuat sub-shell yang dihancurkan setelah perintah Anda ada, jadi jika Anda mengubah direktori di shell - itu tidak mempengaruhi lingkungan Anda di Python.

Selain itu, membuat sub-shell memakan waktu, jadi menggunakan perintah OS secara langsung akan berdampak pada kinerja Anda

EDIT

Saya menjalankan beberapa tes waktu:

In [379]: %timeit os.chmod('Documents/recipes.txt', 0755)
10000 loops, best of 3: 215 us per loop

In [380]: %timeit os.system('chmod 0755 Documents/recipes.txt')
100 loops, best of 3: 2.47 ms per loop

In [382]: %timeit call(['chmod', '0755', 'Documents/recipes.txt'])
100 loops, best of 3: 2.93 ms per loop

Fungsi internal berjalan lebih dari 10 kali lebih cepat

EDIT2

Mungkin ada kasus ketika menjalankan executable eksternal dapat menghasilkan hasil yang lebih baik daripada paket Python - Saya baru ingat sebuah surat yang dikirim oleh seorang rekan saya bahwa kinerja gzip yang dipanggil melalui subproses jauh lebih tinggi daripada kinerja paket Python yang ia gunakan. Tetapi tentu saja tidak ketika kita berbicara tentang paket OS standar meniru perintah OS standar

gunung berapi
sumber
Kebetulan apa yang dilakukan dengan iPython? Tidak mengira Anda bisa menggunakan fungsi khusus yang dimulai dengan %menggunakan juru bahasa normal.
iProgram
@ aPyDeveloper, ya, itu adalah iPython - di Ubuntu. "Ajaib" % timeit adalah berkah - meskipun ada beberapa kasus - sebagian besar dengan pemformatan string - yang tidak dapat diproses
gunung berapi
1
Atau Anda juga dapat membuat skrip python dan kemudian mengetik time <path to script> terminal dan itu akan memberi tahu Anda waktu yang sebenarnya, pengguna dan proses yang diambil. Itu jika Anda tidak memiliki iPython dan Anda memiliki akses ke baris perintah Unix.
iProgram
1
@ aPyDeveloper, saya tidak melihat alasan untuk bekerja keras - ketika saya memiliki iPython di komputer saya
gunung berapi
Benar! Saya memang mengatakan jika Anda tidak memiliki iPython. :)
iProgram
16

Panggilan Shell bersifat spesifik OS sedangkan fungsi modul os Python tidak, dalam sebagian besar kasus. Dan itu menghindari menelurkan subproses.

JoshRomRock
sumber
1
Fungsi modul Python juga menelurkan subproses baru untuk memanggil subkulit baru.
Koderok
7
@Koderok omong kosong, fungsi modul disebut dalam proses
dwurf
3
@Koderok: modul os menggunakan panggilan sistem yang mendasarinya bahwa perintah shell digunakan, itu tidak menggunakan perintah shell. Ini berarti system call os biasanya lebih aman dan lebih cepat (tidak ada string parsing, boo fork, no exec, sebaliknya itu hanya panggilan kernel) daripada perintah shell. Perhatikan bahwa pada kebanyakan kasus, panggilan shell dan panggilan sistem sering memiliki nama yang sama atau sama, tetapi ada yang didokumentasikan secara terpisah; panggilan shell ada di man section 1 (man man section) sedangkan system call yang bernama sama ada di man section 2 (mis. man 2 chmod).
Lie Ryan
1
@wurf, LieRyan: Kasihan! Sepertinya saya salah paham. Terima kasih!
Koderok
11

Jauh lebih efisien. "Shell" hanyalah binari OS lain yang berisi banyak panggilan sistem. Mengapa harus mengeluarkan biaya untuk membuat seluruh proses shell hanya untuk pemanggilan sistem tunggal?

Situasinya bahkan lebih buruk ketika Anda menggunakan os.systemsesuatu yang bukan shell. Anda memulai proses shell yang pada gilirannya memulai executable yang kemudian (dua proses jauhnya) membuat panggilan sistem. Setidaknya subprocessakan menghilangkan kebutuhan untuk proses perantara shell.

Ini tidak spesifik untuk Python, ini. systemdadalah peningkatan waktu startup Linux untuk alasan yang sama: itu membuat sistem yang diperlukan memanggil dirinya sendiri bukannya menelurkan seribu shell.

MSalters
sumber