シェルコマンドを直接実行するのではなく、なぜPythonのosモジュールメソッドを使用するのですか?


157

os.system()またはでコマンドを実行するのではなく、ファイル/ディレクトリの作成、ファイル属性の変更などのOS固有のタスクを実行するためにPythonのライブラリ関数を使用する背後にある動機は何subprocess.call()ですか?

たとえば、なぜ私はos.chmod代わりに使用したいのos.system("chmod...")ですか?

シェルコマンドを直接実行するのではなく、Pythonの利用可能なライブラリメソッドをできるだけ使用する方が「pythonic」であると理解しています。しかし、機能の観点からこれを行う背後にある他の動機はありますか?

ここでは、単純な1行のシェルコマンドの実行についてのみ説明しています。タスクの実行をより詳細に制御する必要がある場合subprocess、たとえば、モジュールを使用するほうが理にかなっていることを理解しています。


6
あなたは基本的に頭に釘を打ちました。参照するOSレベルのタスクは、os.systemを介して呼び出されるだけではなく、独自の機能を保証するほど一般的です。
deweyredman 2015

7
ところで、実行時間を計ろうとしましたか-os.chmodos.system( "chmod ...")。それがあなたの質問の一部に答えるだろうと思います。
火山

61
なぜprintあなたはできるのos.system("echo Hello world!")ですか?
user253751 2015

25
同じ理由で、os.pathパスを手動で処理する代わりに、パスの処理に使用する必要があります。これは、実行するすべてのOSで機能します。
バクリウ

51
「シェルコマンドを直接実行する」ことは、実際にはそれほど直接的ではありません。シェルはシステムへの低レベルのインターフェイスでos.chmodはなくchmod、シェルがプログラムを呼び出すことはありません。を使用os.system('chmod ...')すると、シェルを起動して文字列を解釈し、別の実行可能ファイルを呼び出してC chmod関数を呼び出すことができますが、Cにos.chmod(...)ははるかに直接アクセスしchmodます。
user2357112は2015

回答:


325
  1. それはより速くos.systemそしてsubprocess.callこの単純なものには不要な新しいプロセスを作成します。実際には、os.systemsubprocess.callしてshellシェルである第1の1、および(それが組み込みのようなシェルがない場合は、実行していることをコマンドである第2の1:引数通常、少なくとも二つの新しいプロセスを作成しますtest)。

  2. 一部のコマンドは 、別のプロセスで役に立たない。たとえば、を実行os.spawn("cd dir/")すると、子プロセスの現在の作業ディレクトリは変更されますが、Pythonプロセスは変更されません。os.chdirそのために使用する必要があります。

  3. シェルによって解釈される特殊文字について心配する必要はありません。os.chmod(path, mode)ファイル名が何であっても動作しますが、os.spawn("chmod 777 " + path)ファイル名がのようなものである場合、ひどく失敗します; rm -rf ~。(引数subprocess.callなしで使用すると、これを回避できることに注意してくださいshell。)

  4. ダッシュで始まるファイル名を気にする必要はありません。os.chmod("--quiet", mode)という名前のファイルの権限を変更します--quietが、引数として解釈されるためos.spawn("chmod 777 --quiet")失敗--quietします。これはにも当てはまりますsubprocess.call(["chmod", "777", "--quiet"])

  5. あなたは少ない Pythonの標準ライブラリが対処することになっているので、クロスプラットフォームやクロスシェルの懸念ます。システムにchmodコマンドがありますか?インストールされていますか?サポートすると思われるパラメータをサポートしていますか?osモジュールは、それが不可能なことを可能な場合や文書などのクロスプラットフォームのようにしようとします。

  6. 実行中のコマンドに関心のある出力がある場合は、それを解析する必要があります。これは、コーナーケース(スペース、タブ、改行を含むファイル名)を忘れる可能性があるため、思ったよりもトリッキーです。移植性については気にしないでください。


38
「クロスプラットフォーム」のポイントに追加するには、ディレクトリの一覧表示は、Linuxでは「ls」、Windowsでは「dir」です。ディレクトリの内容を取得することは、非常に一般的な低レベルのタスクです。
Cort Ammon、2015

1
@CortAmmon:「低レベル」相対的である、lsまたはdir同じように、開発者の特定の種類にかなりハイレベルですbashcmd、またはkshあなたが好むまたは何シェル。
セバスチャンマッハ

1
@phresnel:そのように考えたことはありません。私にとって、「OSのカーネルAPIへの直接呼び出し」は非常に低レベルでした。私は自分自身のバイアスで(自然に)それに近づいているので、これについて私を逃れている別の見方があると思います。
Cort Ammon

5
@CortAmmon:そう、それlsはあなたのOSのカーネルAPIへの直接の呼び出しではないので、それより高いレベルです。これは(小さな)アプリケーションです。
Steve Jessop

1
@SteveJessop。「ディレクトリの内容を取得する」ということを低レベルで呼びました。私は考えていないんだlsか、dirしかし、opendir()/readdir()(LinuxのAPI)またはFindFirstFile()/FindNextFile()(Windows APIの)またはFile.listFiles(JavaのAPI)またはDirectory.GetFiles()(C#)を。これらはすべて、OSへの直接呼び出しと密接に関連しています。レジスタに数値をプッシュし、int 13hカーネルモードをトリガーするために呼び出すだけの簡単なものもあります。
Cort Ammon、2015

133

安全です。ここでアイデアを与えるのはスクリプトの例です

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

ユーザーからの入力がtest; rm -rf ~これである場合、ホームディレクトリが削除されます。

このため、組み込み関数を使用する方が安全です。

したがって、システムの代わりにサブプロセスを使用する必要がある理由。


26
または別の見方をすると、Pythonプログラムを作成するのか、シェルスクリプトを作成するPythonプログラムを作成するのが簡単なのでしょうか。:-)
Steve Jessop、

3
私の同僚である@SteveJessopは、私が彼の作成を手助けした小さなPythonスクリプトが、20(!)倍速いtanシェルスクリプトで動作することに驚いていました。出力のリダイレクトはセクシーに見えるかもしれませんが、反復ごとにファイルを開いたり閉じたりする必要があることを説明しました。しかし、困難な方法で物事をやりたいという人もいます-:)
火山

1
@SteveJessop、これはトリックの質問です-実行時までわからないでしょう!:)

60

コマンドの実行時にモジュールまたはosモジュールよりもPythonのより具体的なメソッドを優先する4つの強力なケースがあります。os.systemsubprocess

  • 冗長性 -別のプロセスの生成は冗長であり、時間とリソースを浪費します。
  • 移植性 -多くのosシェルコマンドはOS固有ですが、モジュールのメソッドの多くは複数のプラットフォームで使用できます。
  • 結果の理解 -プロセスを生成して任意のコマンドを実行すると、出力からの結果を解析し、コマンドが何か問題を起こしたどうかとその理由を理解することが強制されます。
  • 安全性 -プロセスは、与えられたコマンドを実行する可能性があります。これは弱い設計であり、osモジュールで特定のメソッドを使用することで回避できます。

冗長性(冗長コードを参照):

実際には、最終的なシステムコール(chmod例では)への途中で冗長な「中間者」を実行しています。この中間者は、新しいプロセスまたはサブシェルです。

からos.system

サブシェルでコマンド(文字列)を実行します...

そしてsubprocess、新しいプロセスを生成するための単なるモジュールです。

これらのプロセスを生成せずに、必要なことを実行できます。

移植性(ソースコードの移植性を参照):

osモジュールの目的は、一般的なオペレーティング・システム・サービスを提供することであり、それはで説明が始まります。

このモジュールは、オペレーティングシステムに依存する機能を使用するポータブルな方法を提供します。

os.listdirWindowsとUNIXの両方で使用できます。この機能にos.system/ を使用しようとすると、subprocess2つの呼び出し(ls/ dir)を維持し、使用しているオペレーティングシステムを確認する必要があります。これは移植性が低く、後でさらに苛立ち引き起こします(出力の処理を参照)。

コマンドの結果を理解する:

ディレクトリ内のファイルを一覧表示するとします。

os.system("ls")/ を使用している場合subprocess.call(['ls'])、プロセスの出力のみを取得できこれは、基本的にファイル名を含む大きな文字列です。

名前にスペースが含まれているファイルと2つのファイルをどのように区別できますか?

ファイルを一覧表示する権限がない場合はどうなりますか?

データをPythonオブジェクトにどのようにマッピングする必要がありますか?

これらは私の頭の上にありますが、これらの問題の解決策があります-なぜあなたのために解決された問題をもう一度解決するのですか?

これは、既に存在し、自由に利用できる実装を繰り返さないことにより、Do n't Repeat Yourself原則(多くの場合「DRY」と呼ばれる)に従う例です。

安全性:

os.systemそしてsubprocess強力です。この力が必要な場合は良いですが、必要でない場合は危険です。あなたが使用する場合はos.listdir、あなたが知っている、それが何か他の、リストファイルを実行するか、エラーを発生させることはできません。を使用しos.systemたりsubprocess、同じ動作を実現したりすると、意図しないことを行う可能性があります。

射出安全(シェル射出の例を参照)

ユーザーからの入力を新しいコマンドとして使用する場合、基本的に彼にシェルを与えました。これは、SQLインジェクションに似ており、ユーザーにDB内のシェルを提供します。

例は、次の形式のコマンドです。

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

これは簡単に実行するために利用することができる任意の入力を使用して任意のコードを:NASTY COMMAND;#最終的に作成します。

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

システムを危険にさらす可能性のあるコマンドはたくさんあります。


3
2.が主な理由です。
jaredad7 2015

23

単純な理由で-シェル関数を呼び出すと、コマンドが存在すると破棄されるサブシェルが作成されるため、シェルでディレクトリを変更しても、Pythonの環境には影響しません。

さらに、サブシェルの作成には時間がかかるため、OSコマンドを直接使用するとパフォーマンスに影響します

編集

いくつかのタイミングテストを実行しました。

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

内部関数は10倍以上速く実行されます

EDIT2

外部の実行可能ファイルを呼び出すと、Pythonパッケージよりも良い結果が得られる場合があります。私は、同僚から送信されたメールで、サブプロセスを通じて呼び出されたgzipのパフォーマンスが、使用したPythonパッケージのパフォーマンスよりもはるかに高いことを思い出しました。しかし、確かに、標準のOSコマンドをエミュレートする標準のOSパッケージについて話しているときはそうではありません。


たぶんそれはiPythonで行われていますか?%通常のインタプリタを使用することから始めて、特別な関数を使用できるとは思いませんでした。
iProgram

@aPyDeveloper、うん、それはiPython-Ubuntuで。"魔法の"%timeitは祝福です-いくつかのケースがあります-ほとんどの場合文字列フォーマットで-それは処理できません
火山

1
または、Pythonスクリプトを作成time <path to script> してターミナルに入力すると、実際にかかった時間、ユーザー時間、処理時間を知ることができます。これは、iPythonがなく、Unixコマンドラインにアクセスできる場合です。
iProgram

1
@aPyDeveloper、私は一生懸命働く理由はないと思います-私のマシンにiPythonがあるとき
火山

ほんとだ!あなたがiPythonを持っていなかったなら私は言った。:)
iProgram

16

ほとんどの場合、シェル呼び出しはOS固有ですが、Python osモジュール関数は固有ではありません。また、サブプロセスの生成を回避します。


1
Pythonモジュール関数は、新しいサブプロセスを起動して新しいサブシェルを呼び出すこともできます。
Koderok、2015

7
@Koderokナンセンス、モジュール関数はインプロセスで呼び出される
dwurf

3
@Koderok:osモジュールは、シェルコマンドが使用する基本的なシステムコールを使用します。シェルコマンドは使用しません。つまり、OSシステムコールは通常、シェルコマンドよりも安全で高速です(文字列の解析なし、ブーイングフォークなし、execなし、代わりに単なるカーネルコールです)。ほとんどの場合、シェル呼び出しとシステム呼び出しの名前は似ているか同じであることがよくありますが、個別に説明されています。シェルコールはmanセクション1(デフォルトのmanセクション)にありますが、同等の名前のシステムコールはmanセクション2(たとえば、man 2 chmod)にあります。
リーライアン

1
@ dwurf、LieRyan:悪い!間違った考えを持っていたようです。ありがとう!
Koderok、2015

11

はるかに効率的です。「シェル」は、多くのシステムコールを含むもう1つのOSバイナリにすぎません。なぜその単一のシステムコールのためだけにシェルプロセス全体を作成するオーバーヘッドが発生するのですか?

os.systemシェルに組み込まれていないものに使用すると、状況はさらに悪化します。シェルプロセスを開始すると、実行可能プログラムが開始され、次に(2プロセス離れた)システムコールが実行されます。少なくともsubprocess、シェルの中間プロセスの必要性はなくなりました。

これはPythonに固有のものではありません。systemd同じ理由でLinuxの起動時間を大幅に改善します。これにより、必要なシステムコールが、1,000シェルを生成する代わりに自動的に呼び出されます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.