Windowsコマンドプロンプト(cmd.exe
)にはオプションの /s
パラメーター があり、これは/c
(特定のコマンドを実行してから終了する)または/k
(特定のコマンドを実行してから、シェルプロンプトを表示します)。この/s
パラメーターは、明らかに不可解な引用符の処理に関係しています。
ドキュメント は紛らわしいですが、私が知る限り、cmd /c
something
、およびsomething
に引用符が含まれている場合、デフォルトでcmd
はそれらの引用符を取り除くことがあります。 /s
は、そのままにしておくように指示します。
私が理解できないのは、/s
(「デフォルトの引用除去動作を抑制する」)が必要になるのはそれだけだからです。特定の難解な条件の下で引用符のみを削除します。これらの条件の1つは、/c
の後の最初の文字が引用符でなければならないことです。したがって、引数を囲む引用符は削除されません。実行中のEXEへのパスを囲む引用符を削除するか、コマンドライン全体を囲むか(またはコマンドラインの前半部分を囲むと、奇妙になります)。
cmd /c "c:\tools\foo.exe" arg1 arg2
の場合、引用符は不要であり、cmd
がそれらを削除する場合は問題ありません。 (パスの名前にスペースが含まれている場合は削除されません-これは難解なルールのもう1つです。)引用符の削除を抑制する理由は想像できないので、/s
は不要のようです。cmd /c "foo.exe arg1 arg2"
の場合、システムにはfoo.exe arg1 arg2
という名前のEXEがないため、引用符を削除する必要があるようです。したがって、/s
を使用した引用符の削除をオプトアウトすると、実際に問題が発生するようです。 (しかし、実際には、それは物事を壊しません:cmd /s /c "foo.exe arg1 arg2"
はうまく動作します。)/s
に微妙な点はありますか?いつ必要になるのでしょうか?いつ違いが生じるのでしょうか?
Cmd/Sは、「引用符の引用」を心配する必要がないため、非常に便利です。 /C
引数は、「プロンプトで入力したかのようにこのコマンドを実行してから終了する」ことを意味することを思い出してください。
したがって、CMD.exeに渡す複雑なコマンドがある場合は、CMDの引数引用ルールを覚えて、すべての引用を適切にエスケープするか、/S
を使用して、特別な非解析ルールをトリガーする必要があります。 「最初と最後の"
を削除し、他のすべての文字を変更せずに実行するコマンドとして扱います」.
別のプログラムを直接呼び出すのではなく、CMDシェルの機能を利用したい場所で使用します。たとえば、環境変数の展開、出力または入力のリダイレクト、またはCMD.exeビルトインの使用。
例:
組み込みのシェルを使用:プロンプトでDEL /Q/S "%TMP%\TestFile"
と入力した場合と同じように実行されます:
CMD.exe /S /C " DEL /Q/S "%TMP%\TestFile" "
これにより、SomeCommand.exeが実行され、標準出力が一時ファイルにリダイレクトされ、標準エラーが同じ場所にリダイレクトされます。
CMD.exe /S /C " "%UserProfile%\SomeCommand.exe" > "%TMP%\TestOutput.txt" 2>&1 "
それで、/S
は何を余分に与えますか?主に、引用符を引用することを心配する必要がなくなります。また、たとえば環境変数に引用符が含まれているかどうかわからない場合にも役立ちます。 /S
と言って、最初と最後に余分な引用符を付けてください。
漠然とした関連:Bourne Shellの$ *。
一部の背景
Main()の引数のリストはC主義とUnix主義であることを思い出してください。 Unix/Linuxシェル(例:Bourne Shellなど)は、コマンドラインを解釈し、引数を引用符で囲まず、*
などのワイルドカードをファイルのリストに展開し、引数のリストを呼び出し先プログラムに渡します。
だからあなたが言うなら:
$ vi *.txt
Viコマンドは、たとえば次の引数を確認します。
vi
a.txt
b.txt
c.txt
d.txt
これは、unix/linuxが「引数のリスト」に基づいて内部的に動作するためです。
最終的にCP/MおよびVAXから派生するWindowsは、このシステムを内部で使用しません。オペレーティングシステムにとって、コマンドラインは単なる文字列です。コマンドラインを解釈し、ファイルグロブ(*
など)を展開し、引用符で囲まれていない引数を処理するのは、呼び出されたプログラムの責任です。
そのため、Cが期待する引数は、Cランタイムライブラリによってハッキングする必要があります。オペレーティングシステムは引数を含む単一の文字列のみを提供します。言語がCでない場合(またはC言語である場合でも)は、シェルの規則に従って引用されたスペース区切りの引数としてではなく、まったく異なるものとして解釈される場合があります。
これがどのように違いを生むことができるかの例です。
c:\Program.exe
とc:\Program Files\foo.exe
の2つの実行可能ファイルがあるとします。
あなたが言うなら
cmd /c "c:\Program Files\foo"
foo.exe
(引数なし)を実行しますが、
cmd /s /c "c:\Program Files\foo"
Program.exe
を引数としてFiles\foo
を実行します。
(奇妙なことに、最初の例では、foo.exe
が存在しなかった場合、Program.exe
が代わりに実行されます。)
補遺:入力する場合
c:\Program Files\foo
コマンドプロンプトで、Program.exe
(cmd/cだけで発生する)ではなく、foo.exe
(cmd/s/cで発生する)を実行します。したがって、/ sを使用する理由の1つは、コマンドプロンプトで入力された場合とまったく同じ方法でコマンドが解析されるようにする場合です。これは、リンク先の質問Michael Burrのシナリオで、cmd.exeがバッチファイルまたはコマンドライン自体からではなくCreateProcessによって起動されるシナリオで望ましい可能性が高くなります。
つまり、あなたが言うなら
CreateProcess("cmd.exe", "cmd /s /c \"" MY_COMMAND "\"", ...)
文字列MY_COMMAND
は、コマンドプロンプトで入力された場合とまったく同じように解析されます。ユーザーからコマンドライン入力を取得している場合、またはアプリケーションが提供するコマンドラインを処理するライブラリーである場合は、おそらく良い考えです。たとえば、Cランタイムライブラリのsystem()関数はこの方法で実装できます。