os.system() 呼び出しをエスケープするにはどうすればよいですか?
質問
os.system() を使用する場合、多くの場合、コマンドにパラメータとして渡されるファイル名やその他の引数をエスケープする必要があります。これどうやってするの?複数のオペレーティング システム/シェルで動作するものが望ましいですが、特に bash では動作します。
現在次のことを行っていますが、これにはライブラリ関数、または少なくともよりエレガント/堅牢/効率的なオプションが必要であると確信しています。
def sh_escape(s):
return s.replace("(","\\(").replace(")","\\)").replace(" ","\\ ")
os.system("cat %s | grep something | sort > %s"
% (sh_escape(in_filename),
sh_escape(out_filename)))
編集: 私は引用符を使用するという単純な答えを受け入れましたが、なぜそれを思いつかなかったのかわかりません。私が Windows 出身だからだと思いますが、' と " の動作は少し異なります。
セキュリティに関しては、その懸念は理解できますが、この場合、os.system() が提供する迅速かつ簡単な解決策に興味があり、文字列のソースはユーザーが生成したものではないか、少なくともユーザーによって入力されたものです。信頼できるユーザー (私)。
解決
これが私が使っているものです:
def shellquote(s):
return "'" + s.replace("'", "'\\''") + "'"
シェルは常に引用符で囲まれたファイル名を受け入れ、問題のプログラムに渡す前に周囲の引用符を削除します。特に、これにより、スペースやその他の種類の厄介なシェル メタ文字が含まれるファイル名に関する問題が回避されます。
アップデート:Python 3.3 以降を使用している場合は、次を使用します。 シュレックス引用 自分で巻く代わりに。
他のヒント
shlex.quote()
Python 3以降はやりたいことを実行します。
(使用 pipes.quote
Python 2 と Python 3 の両方をサポートする)
おそらく使用する特別な理由があるでしょう os.system()
. 。しかし、そうでない場合は、おそらく、 subprocess
モジュール. 。パイプを直接指定し、シェルの使用を回避できます。
以下より PEP324:
Replacing shell pipe line ------------------------- output=`dmesg | grep hda` ==> p1 = Popen(["dmesg"], stdout=PIPE) p2 = Popen(["grep", "hda"], stdin=p1.stdout, stdout=PIPE) output = p2.communicate()[0]
多分 subprocess.list2cmdline
より良いショットですか?
Pipes.quote は実際には Python 2.5 および Python 3.1 では壊れており、安全に使用できないことに注意してください。長さ 0 の引数は処理されません。
>>> from pipes import quote
>>> args = ['arg1', '', 'arg3']
>>> print 'mycommand %s' % (' '.join(quote(arg) for arg in args))
mycommand arg1 arg3
見る Python の問題 7476;この問題は Python 2.6 および 3.2 以降で修正されています。
os.system は、ユーザーに設定されているコマンド シェルを呼び出すだけだと思います。そのため、プラットフォームに依存しない方法で実行できるとは思いません。私のコマンド シェルは、bash、emacs、ruby、さらには quake3 まで何でも可能です。これらのプログラムの中には、渡される引数の種類を予期していないものもあります。また、たとえ予期していたとしても、同じ方法でエスケープを行うという保証はありません。
知らせ:これは Python 2.7.x の答えです。
による ソース, pipes.quote()
という方法です」文字列を単一の引数として確実に引用符で囲みます。 /bin/sh」。(そうですが バージョン 2.7 以降非推奨になりました そして最終的には Python 3.3 として公開されました。 shlex.quote()
関数。)
の上 一方, subprocess.list2cmdline()
という方法です」と同じルールを使用して、一連の引数をコマンド ライン文字列に変換します。 MS C ランタイム".
これは、コマンドラインの文字列を引用符で囲むプラットフォームに依存しない方法です。
import sys
mswindows = (sys.platform == "win32")
if mswindows:
from subprocess import list2cmdline
quote_args = list2cmdline
else:
# POSIX
from pipes import quote
def quote_args(seq):
return ' '.join(quote(arg) for arg in seq)
使用法:
# Quote a single argument
print quote_args(['my argument'])
# Quote multiple arguments
my_args = ['This', 'is', 'my arguments']
print quote_args(my_args)
私が使用する関数は次のとおりです。
def quote_argument(argument):
return '"%s"' % (
argument
.replace('\\', '\\\\')
.replace('"', '\\"')
.replace('$', '\\$')
.replace('`', '\\`')
)
あれは:私は常に引数を二重引用符で囲み、二重引用符内の特殊な文字のみをバックスラッシュで囲みます。
system コマンドを使用する場合は、os.system() 呼び出しに含まれる内容をホワイトリストに登録してみます。例えば..
clean_user_input re.sub("[^a-zA-Z]", "", user_input)
os.system("ls %s" % (clean_user_input))
subprocess モジュールの方がより良いオプションであり、可能な限り os.system/subprocess のようなものの使用を避けることをお勧めします。
本当の答えは次のとおりです。使用しないでください os.system()
そもそも。使用 subprocess.call
代わりに、エスケープされていない引数を指定します。