os.system()
またはsubprocess.call()
を介してコマンドを実行するだけでなく、ファイル/ディレクトリの作成、ファイル属性の変更など、OS固有のタスクを実行するためにPythonのライブラリ関数を使用する背後にある動機を理解しようとしています。
たとえば、なぜos.system("chmod...")
を行う代わりにos.chmod
を使用したいのでしょうか?
シェルコマンドを直接実行するだけでなく、Pythonの利用可能なライブラリメソッドを可能な限り使用する方が「Pythonに近い」ことを理解しています。しかし、機能の観点からこれを行う他の動機はありますか?
ここでは、単純な1行のシェルコマンドの実行についてのみ説明しています。タスクの実行をさらに制御する必要がある場合、たとえば、subprocess
モジュールを使用する方が理にかなっていることがわかります。
faster、os.system
およびsubprocess.call
は、この単純な処理には不要な新しいプロセスを作成します。実際、os.system
およびShell
引数を持つsubprocess.call
は通常、少なくとも2つの新しいプロセスを作成します。最初のプロセスはシェルで、2番目のプロセスは実行中のコマンドです( test
のようなシェル組み込みでない場合。
一部のコマンドは別のプロセスでは役に立たないです。たとえば、os.spawn("cd dir/")
を実行すると、子プロセスの現在の作業ディレクトリは変更されますが、Pythonプロセスの現在の作業ディレクトリは変更されません。そのためにはos.chdir
を使用する必要があります。
シェルによる特別な解釈される文字について心配する必要はありません。 os.chmod(path, mode)
はファイル名が何であっても機能しますが、ファイル名が; rm -rf ~
のような場合、os.spawn("chmod 777 " + path)
はひどく失敗します。 (Shell
引数なしでsubprocess.call
を使用すると、この問題を回避できることに注意してください。)
ダッシュで始まるファイル名について心配する必要はありません。 os.chmod("--quiet", mode)
は--quiet
という名前のファイルのパーミッションを変更しますが、--quiet
は引数として解釈されるため、os.spawn("chmod 777 --quiet")
は失敗します。これは、subprocess.call(["chmod", "777", "--quiet"])
にも当てはまります。
Pythonの標準ライブラリがそれを処理することになっているため、クロスプラットフォームおよびクロスシェルの懸念は少なくなります。システムにchmod
コマンドがありますか?インストールされていますか?サポートすると予想されるパラメーターをサポートしていますか? os
モジュールは、可能な限りクロスプラットフォームになるように試み、それが不可能な場合は文書化します。
実行しているコマンドにoutputが含まれている場合は、解析する必要があります。これは、コーナーケース(スペース、タブ、改行を含むファイル名を忘れる可能性があるため、思ったよりもトリッキーです)移植性を気にしない場合でも)。
より安全です。ここであなたにアイデアを与えるために、スクリプトの例があります
import os
file = raw_input("Please enter a file: ")
os.system("chmod 777 " + file)
ユーザーからの入力がtest; rm -rf ~
だった場合、これはホームディレクトリを削除します。
これが、組み込み関数を使用する方が安全な理由です。
したがって、なぜシステムの代わりにサブプロセスを使用する必要があるのでしょうか。
os
モジュール内のPythonのより具体的なメソッドを os.system
または subprocess
を使用するよりも優先する4つの強力なケースがあります。 コマンド実行時のモジュール:
os
モジュールの多くのメソッドは複数のプラットフォームで使用できますが、多くのシェルコマンドはOS固有です。os
モジュールで特定のメソッドを使用することで回避できます。実際には、最終的なシステムコール(例ではchmod
)に向かう途中で、冗長な「ミドルマン」を実行しています。この中間者は新しいプロセスまたはサブシェルです。
os.system
から:
サブシェルでコマンド(文字列)を実行します...
subprocess
は、新しいプロセスを生成するための単なるモジュールです。
これらのプロセスを生成することなく、必要なことを実行できます。
os
モジュールの目的は、一般的なオペレーティングシステムサービスを提供することであり、その説明は次で始まります。
このモジュールは、オペレーティングシステムに依存する機能を使用するポータブルな方法を提供します。
os.listdir
は、ウィンドウとUNIXの両方で使用できます。この機能にos.system
/subprocess
を使用しようとすると、2つの呼び出し(ls
/dir
の場合)を維持し、使用しているオペレーティングシステムを確認する必要があります。これは移植性が低く、willは後でさらにフラストレーションを引き起こします(出力の処理を参照)。
ディレクトリ内のファイルを一覧表示するとします。
os.system("ls")
/subprocess.call(['ls'])
を使用している場合、プロセスの出力のみを取得できます。これは基本的にファイル名を含む大きな文字列です。
2つのファイルの名前にスペースを含むファイルをどのように伝えることができますか?
ファイルをリストする権限がない場合はどうなりますか?
データをどのようにpythonオブジェクトにマッピングする必要がありますか?
これらは私の頭上にあるだけであり、これらの問題の解決策はありますが、なぜあなたのために解決された問題を再び解決するのですか?
これは、 Do n't Repeat Yourself 原則(「DRY」と呼ばれることが多い)に従うnotの例です。すでに存在し、自由に利用できる実装。
os.system
とsubprocess
は強力です。このパワーが必要なときは良いのですが、必要ないときは危険です。 os.listdir
を使用すると、know以外のことはできません。ファイルをリストするか、エラーが発生します。 os.system
またはsubprocess
を使用して同じ動作を実現すると、意図しないことを実行してしまう可能性があります。
注入の安全性( シェル注入の例を参照 ):
ユーザーからの入力を新しいコマンドとして使用する場合、基本的にユーザーにシェルを与えました。これは、ユーザーにDBのシェルを提供するSQLインジェクションによく似ています。
例は、次の形式のコマンドです。
# ... read some user input
os.system(user_input + " some continutation")
これは、入力:NASTY COMMAND;#
を使用してanyの任意のコードを実行し、最終的に作成するために簡単に活用できます。
os.system("NASTY COMMAND; # some continuation")
システムを危険にさらす可能性のあるコマンドは多数あります。
単純な理由-シェル関数を呼び出すと、コマンドが存在した後に破棄されるサブシェルが作成されるため、シェルでディレクトリを変更しても、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パッケージについて話しているときはそうではありません
シェル呼び出しはOS固有ですが、ほとんどの場合、Python osモジュール関数はそうではありません。そして、サブプロセスの生成を回避します。
はるかに効率的です。 「シェル」は、多くのシステムコールを含む単なる別のOSバイナリです。単一のシステムコールだけのためにシェルプロセス全体を作成するオーバーヘッドが発生するのはなぜですか?
シェル組み込みではないものにos.system
を使用すると、状況はさらに悪化します。シェルプロセスを開始すると、実行可能ファイルが開始され、次に(2プロセス離れて)システムコールが実行されます。少なくともsubprocess
は、シェルの中間プロセスの必要性を排除します。
これはPythonに固有のものではありません。 systemd
は、同じ理由でLinuxの起動時間を大幅に改善します。1000のシェルを生成する代わりに、必要なシステムがそれ自体を呼び出すようにします。