web-dev-qa-db-ja.com

バッチスクリプトでの二重引用符のエスケープ

バッチファイルのパラメーター内のすべての二重引用符をエスケープされた二重引用符に置き換えるにはどうすればよいですか?これは現在のバッチファイルで、文字列内のすべてのコマンドラインパラメーターを展開します。

@echo off
call bash --verbose -c "g++-linux-4.1 %*"

次に、その文字列を使用してCygwinのbashを呼び出し、Linuxクロスコンパイラーを実行します。残念ながら、バッチファイルに渡されるこれらのようなパラメーターを取得しています。

"launch-linux-g++.bat" -ftemplate-depth-128 -O3 -finline-functions 
-Wno-inline -Wall  -DNDEBUG   -c 
-o "C:\Users\Me\Documents\Testing\SparseLib\bin\Win32\LinuxRelease\hello.o" 
"c:\Users\Me\Documents\Testing\SparseLib\SparseLib\hello.cpp"

渡された最初のパスを囲む最初の引用符は、GCCに渡される文字列を途中で終了し、残りのパラメーターを直接bashに渡すことです(これは見事に失敗します)。

パラメータを単一の文字列に連結し、引用符をエスケープしてうまく機能する場合は想像できますが、これを行う方法を判断するのは困難です。誰か知っている?

81
eplawless

Googleは最終的に答えを思いついた。バッチでの文字列置換の構文は次のとおりです。

set v_myvar=replace me
set v_myvar=%v_myvar:ace=icate%

「私を複製する」を生成します。スクリプトは次のようになります。

@echo off
set v_params=%*
set v_params=%v_params:"=\"%
call bash -c "g++-linux-4.1 %v_params%"

これにより、"のすべてのインスタンスが\"に置き換えられ、bash用に適切にエスケープされます。

22
eplawless

バッチスクリプトのエスケープ文字は^。ただし、二重引用符で囲まれた文字列の場合は、引用符を2重にします。

"string with an embedded "" character"
92
Eclipse

mklement0の優れた回答 への追加として:

ほとんどすべての実行可能ファイルは_\"_をエスケープされた_"_として受け入れます。ただし、cmdでの安全な使用は、DELAYEDEXPANSIONを使用した場合にのみ可能です。
リテラル_"_を明示的にプロセスに送信するには、_\"_を環境変数に割り当て、引用符を渡す必要があるときにその変数を使用します。例:

_SETLOCAL ENABLEDELAYEDEXPANSION
set q=\"
child "malicious argument!q!&whoami"
_

注_SETLOCAL ENABLEDELAYEDEXPANSION_は、バッチファイル内でのみ機能するようです。インタラクティブセッションでDELAYEDEXPANSIONを取得するには、_cmd /V:ON_を起動します。

バッチファイルがDELAYEDEXPANSIONで動作しない場合、一時的に有効にすることができます。

_::region without DELAYEDEXPANSION

SETLOCAL ENABLEDELAYEDEXPANSION
::region with DELAYEDEXPANSION
set q=\"
echoarg.exe "ab !q! & echo danger"
ENDLOCAL

::region without DELAYEDEXPANSION
_

_""_としてエスケープされた引用符を含む変数から動的コンテンツを渡したい場合、展開時に_""_を_\"_に置き換えることができます:

_SETLOCAL ENABLEDELAYEDEXPANSION
foo.exe "danger & bar=region with !dynamic_content:""=\"! & danger"
ENDLOCAL
_

この置換は_%...%_スタイルの展開では安全ではありません!

[〜#〜] op [〜#〜] _bash -c "g++-linux-4.1 !v_params:"=\"!"_の場合は安全なバージョンです。


何らかの理由でDELAYEDEXPANSIONを一時的に有効にすることもオプションではない場合は、以下をお読みください。

Cmd内から_\"_を使用することは、たまにではなく、常に特殊文字をエスケープする必要がある場合、少し安全です。 (キャレットが一貫していれば、キャレットを忘れる可能性は低くなります...)

これを実現するには、引用符の前にキャレット(_^"_)を付けます。リテラルとして子プロセスに到達する引用符は、さらにバックスラッシュ(_\^"_)でエスケープする必要があります。 [〜#〜] all [〜#〜]シェルのメタ文字も_^_でエスケープする必要があります。 _&_ => _^&_; _|_ => _^|_; _>_ => _^>_;等.

例:

_child ^"malicious argument\^"^&whoami^"
_

ソース: 誰もがコマンドライン引数を間違った方法で引用する 、「引用のより良い方法」を参照


動的コンテンツを渡すには、次のことを確認する必要があります。
変数を含むコマンドの部分は、_cmd.exe_で「引用」されていると見なされる必要があります(変数に引用符が含まれている場合は不可能です-書かない_%var:""=\"%_))。これを実現するために、変数の前の最後の_"_と変数の後の最初の_"_は_^_-- escapedではありません。これら2つの_"_の間のcmdメタ文字はエスケープしないでください。例:

_foo.exe ^"danger ^& bar=\"region with %dynamic_content% & danger\"^"
_

これは、_%dynamic_content%_に一致しない引用符を含めることができる場合、安全ではありません。

8
T S