web-dev-qa-db-ja.com

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

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

たとえば、なぜos.system("chmod...")を行う代わりにos.chmodを使用したいのでしょうか?

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

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

156
Koderok
  1. fasteros.systemおよびsubprocess.callは、この単純な処理には不要な新しいプロセスを作成します。実際、os.systemおよびShell引数を持つsubprocess.callは通常、少なくとも2つの新しいプロセスを作成します。最初のプロセスはシェルで、2番目のプロセスは実行中のコマンドです( testのようなシェル組み込みでない場合。

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

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

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

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

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

325
Flimm

より安全です。ここであなたにアイデアを与えるために、スクリプトの例があります

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

ユーザーからの入力がtest; rm -rf ~だった場合、これはホームディレクトリを削除します。

これが、組み込み関数を使用する方が安全な理由です。

したがって、なぜシステムの代わりにサブプロセスを使用する必要があるのでしょうか。

133
iProgram

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

  • Redundancy-別のプロセスの生成は冗長であり、時間とリソースを浪費します。
  • Portability-osモジュールの多くのメソッドは複数のプラットフォームで使用できますが、多くのシェルコマンドはOS固有です。
  • 結果の理解-任意のコマンドを実行するプロセスを生成すると、出力から結果を解析し、ifを理解する必要があります。 /およびwhyコマンドが何か間違ったことをした。
  • Safety-プロセスは、与えられたコマンドを潜在的に実行できます。これは弱い設計であり、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.systemsubprocessは強力です。このパワーが必要なときは良いのですが、必要ないときは危険です。 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")

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

60
Reut Sharabani

単純な理由-シェル関数を呼び出すと、コマンドが存在した後に破棄されるサブシェルが作成されるため、シェルでディレクトリを変更しても、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パッケージについて話しているときはそうではありません

23
volcano

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

16
JoshRomRock

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

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

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

11
MSalters