5

コマンドライン引数として安全に使用するために文字列をエスケープする最良の方法は何ですか?subprocess.Popenusingがこのusingを処理することは知っていますlist2cmdline()が、paramikoでは正しく機能しないようです。例:

from subprocess import Popen
Popen(['touch', 'foo;uptime']).wait()

これにより、文字通りという名前のファイルが作成されますfoo;uptime。これが私が欲しいものです。比較:

from paramiko import SSHClient()
from subprocess import list2cmdline
ssh = SSHClient()
#... load host keys and connect to a server
stdin, stdout, stderr = ssh.exec_command(list2cmdline(['touch', 'foo;uptime']))
print stdout.read()

これにより、というファイルが作成されfoo、リモートホストの稼働時間が出力されます。uptime最初のコマンドの引数の一部として使用するのではなく、2番目のコマンドとして実行されましたtouch。これは私が望むものではありません。

に送信する前後にバックスラッシュでセミコロンをエスケープしようとしましたlist2cmdlineが、結局は。というファイルになりましたfoo\;uptime

uptimeまた、の代わりにスペースを含むコマンドを使用すると、正しく機能します。

stdin, stdout, stderr = ssh.exec_command(list2cmdline(['touch', 'foo;echo test']))
print stdout.read()

これにより、引用符で囲まれているfoo;echo testため、文字通り呼び出されるファイルが作成されます。list2cmdline

また、試しpipes.quote()てみたところ、と同じ効果がありましたlist2cmdline

編集:明確にするために、受信する入力データに関係なく、リモートホストで実行されるコマンドが1つだけであることを確認する必要があります。つまり、、、、バックティックなどの文字をエスケープする必要が;あり&ます。

4

3 に答える 3

10

リモートユーザーがPOSIXシェルを持っているとすると、これは機能するはずです。

def shell_escape(arg):
    return "'%s'" % (arg.replace(r"'", r"'\''"), )

なぜこれが機能するのですか?

POSIXシェルの一重引用符は次のように定義されます。

文字を一重引用符('')で囲むと、一重引用符内の各文字のリテラル値が保持されます。一重引用符は一重引用符内では発生しません。

ここでの考え方は、文字列を一重引用符で囲むことです。これだけでも、ほぼ十分です---一重引用符を除くすべての文字が文字通りに解釈されます。一重引用符の場合は、一重引用符で囲まれた文字列(最初の)を削除し、一重引用'符()を追加してから、一重引用符で\'囲まれた文字列(最後)を再開し'ます。

これは何で機能しますか?

これは、すべてのPOSIXシェルで機能するはずです。ダッシュとbashでテストしました。Solaris 5.10 /bin/sh(POSIX互換ではないと私は信じており、仕様が見つかりませんでした)も機能しているようです。

任意のリモートホストの場合、これは不可能だと思います。sshリモートユーザーのシェル(で構成されている/etc/passwdか同等のもの)を使用してコマンドを実行すると思います。リモートユーザーが実行している可能性がある場合、たとえば、/usr/bin/python引用git-shellスキームがシェル間の不整合に遭遇する可能性があるだけでなく、コマンドの実行も失敗する可能性があります。

csh / tcsh

もう少し問題なのは、リモートユーザーが実行されている可能性です。これは、tcsh実際に実際に実行していて、paramikoが機能することを期待している場合があるexec_commandためです。(シェルとしてのユーザーは/usr/bin/pythonおそらくそのような期待を持っていません...)

tcshはほとんど機能しているようです。しかし、私はそれが幸せになるように改行を引用する方法を理解することはできません。一重引用符で囲まれた文字列に改行を含めると、tcshが不幸になるようです。

$ tcsh -c $'echo \'foo\nbar\''
Unmatched '.
Unmatched '.

改行を除いて、私が試したものはすべてtcshで機能するようです(一重引用符、二重引用符、円記号、埋め込みタブ、アスタリスクなどを含む)。

シェルエスケープのテスト

エスケープスキームがある場合は、次のテストを行うことができます。

  • エスケープシーケンス(、、\n... \t
  • 引用符('、、"\
  • グロブ文字(*、、、?など[]
  • |ジョブ制御とパイプライン(、、、、、&... )||&&
  • 改行

改行は特筆に値します。re.escapeソリューションはこの権利を処理しません---英数字以外の文字をエスケープし、POSIXシェルはエスケープされた改行(つまり、Pythonでは2文字の文字列"\\\n")を単一の改行文字ではなくゼロ文字と見なします。正規表現用に設計されたものを使用してシェルのエスケープを行うのは怖いですが、他のすべてのケースを正しく処理すると思います。 うまくいくかもしれませんが、ルールの微妙なケースやシェルエスケープルール(改行など)、またはAPIの将来の変更の可能性re.escapeについて心配しています。re.escape

また、エスケープシーケンスはさまざまな段階で処理される可能性があるため、テストが複雑になることにも注意してください。プログラムが何を実行するかではなく、シェルがプログラムに何を渡すかだけを気にします。使用するprintf "%s\n" escaped-string-to-testのがおそらく最善の策です。echo驚くほどうまく機能しません。一気に、echo組み込みプロセスのバックスラッシュはのようにエスケープします\n。通常は安全に使用/bin/echoできますが、テストしたSolaris 5.10マシンでは、のようなシーケンスも処理します\n

于 2012-12-09T11:09:54.177 に答える
1

list2cmdline()SSHを使用して通信しているPOSIXコマンドラインとは異なるルールを持つMicrosoftコマンドラインを対象としているため、成功していません。

代わりに、ネイティブPythonルーチンを使用pipes.quote()し、コマンドの各引数に個別に適用するように注意してください。これにより、SSH用の実用的なコマンドラインが提供されます。

from pipes import quote
command = ['touch', 'foo;uptime']
print ' '.join(quote(s) for s in command)

;出力は、文字を保護するために2番目の引数を注意深く引用しています。

touch 'foo;uptime'
于 2016-07-31T20:08:57.300 に答える
-3

re.escape()私が探しているものです。

再。*エスケープ(**文字列*)

英数字以外のすべてをバックスラッシュした**文字列*を返します。

例:

from paramiko import SSHClient()
from subprocess import list2cmdline
import re
ssh = SSHClient()
#... load host keys and connect to a server
stdin, stdout, stderr = ssh.exec_command(' '.join(['touch', re.escape('foo;uptime')]))

これにより、サーバー上に、というファイルが作成されますfoo;uptime。これが私が欲しいものです。

私は私が考えることができるすべてのシェルメタキャラクターを試しました、そしてそれはうまくいきます:

stdin, stdout, stderr = ssh.exec_command(' '.join(['touch', re.escape('test;rm foo&echo "Uptime: `uptime`"')]))
于 2010-07-07T02:22:53.390 に答える