12

次の2つの関数を作成し、Windows Script Host内で実行されているJavaScriptから2番目の関数( "callAndWait")を呼び出します。私の全体的な目的は、あるコマンドラインプログラムを別のコマンドラインプログラムから呼び出すことです。つまり、cscriptを使用して最初のスクリプトを実行してから、そのスクリプトから別のスクリプト(Ant)を実行しようとしています。

function readAllFromAny(oExec)
{
     if (!oExec.StdOut.AtEndOfStream)
          return oExec.StdOut.ReadLine();

     if (!oExec.StdErr.AtEndOfStream)
          return "STDERR: " + oExec.StdErr.ReadLine();

     return -1;
}

// Execute a command line function....
function callAndWait(execStr) {
 var oExec = WshShell.Exec(execStr);
  while (oExec.Status == 0)
 {
  WScript.Sleep(100);
  var output;
  while ( (output = readAllFromAny(oExec)) != -1) {
   WScript.StdOut.WriteLine(output);
  }
 }

}

残念ながら、プログラムを実行しても、呼び出されたプログラムが何をしているかについてすぐにフィードバックが得られません。代わりに、出力が適切に開始され、元のプログラムが終了するまで待機することもあれば、デッドロックしたように見えることもあります。私が本当にやりたいのは、生成されたプロセスが実際に呼び出しプロセスと同じStdOutを共有するようにすることですが、それを行う方法がわかりません。oExec.StdOut=WScript.StdOutを設定するだけでは機能しません。

起動プロセスのStdOutとStdErrを共有するプロセスを生成する別の方法はありますか?「WshShell.Run()」を使用してみましたが、「許可が拒否されました」というエラーが表示されます。プログラムを実行するためだけにWindows環境の構成を変更するようにクライアントに指示する必要がないため、問題があります。

私に何ができる?

4

5 に答える 5

11

Code Master Bob が言うように、ノンブロッキング IO がないため、この方法でスクリプト エンジンの StdErr と StdOut から読み取ることはできません。StdOut から読み取ろうとしているときに、呼び出されたプロセスが StdErr のバッファー (約 4KB) をいっぱいにすると、デッドロックまたはハングします。StdOut を待っている間に飢え、StdErr からの読み取りを待ってブロックされます。

実際の解決策は、次のように StdErr を StdOut にリダイレクトすることです。

sCommandLine = """c:\Path\To\prog.exe"" Argument1 argument2"
Dim oExec
Set oExec = WshShell.Exec("CMD /S /C "" " & sCommandLine & " 2>&1 """)

つまり、CreateProcess に渡されるものは次のとおりです。

CMD /S /C " "c:\Path\To\prog.exe" Argument1 argument2 2>&1 "

これにより、コマンド ラインを解釈する CMD.EXE が呼び出されます。/S /C最初と最後の引用符が取り除かれ、残りがそのまま使用され、CMD.EXE によって実行されるように、特別な解析規則を呼び出します。したがって、CMD.EXE はこれを実行します。

"c:\Path\To\prog.exe" Argument1 argument2 2>&1

呪文はの StdErr を StdOut に2>&1リダイレクトします。prog.exeCMD.EXE は終了コードを伝達します。

StdOut から読み取り、StdErr を無視することで成功できるようになりました。

欠点は、StdErr と StdOut の出力が混在することです。それらが認識可能である限り、おそらくこれで作業できます。

于 2012-01-30T11:30:35.180 に答える
4

この状況で役立つ可能性のある別の手法は、コマンドの標準エラーストリームをリダイレクトして、標準出力に付随させることです。これを行うには、execStr文字列の先頭に「%comspec%/ c」を追加し、末尾に「2>&1」を追加します。つまり、実行するコマンドを次のように変更します。

zzz

に:

%comspec% /c zzz 2>&1 

「2>&1」は、StdErr出力(ファイル記述子2)がStdOutストリーム(ファイル記述子1)に書き込まれるようにするリダイレクト命令です。コマンドラインリダイレクトを理解するのはコマンドインタープリターであるため、「%comspec%/c」の部分を含める必要があります。http://technet.microsoft.com/en-us/library/ee156605.aspxを参照してください
。「cmd」の代わりに「%comspec%」を使用すると、より広範囲のWindowsバージョンへの移植性が得られます。コマンドに引用符で囲まれた文字列引数が含まれている場合、それらを正しく理解するのは難しいかもしれません。「/c」の後のcmdが引用符を処理する方法の仕様は不完全なようです。

これにより、スクリプトはStdOutストリームを読み取るだけで済み、標準出力と標準エラーの両方を受け取ります。これを「netstopwuauserv」で使用しました。これは、成功するとStdOutに書き込み(サービスが実行されている場合)、失敗するとStdErrに書き込みます(サービスがすでに停止している場合)。

于 2012-01-17T20:13:03.980 に答える
2

まず、ループは常に最初から読み取ろうとするという点で壊れていますoExec.StdOut。実際の出力がない場合は、出力されるまでハングします。true になるまで (おそらく子が終了したとき)、StdErr出力は表示されません。StdOut.atEndOfStream残念ながら、スクリプト エンジンにはノンブロッキング I/O の概念がありません。readこれは、バッファにデータがない場合に呼び出してすぐに返すことを意味します。したがって、このループを希望どおりに動作させる方法はおそらくありません。2番、WShell.Run子プロセスの標準 I/O にアクセスするためのプロパティやメソッドは提供されません。別のウィンドウに子を作成し、戻りコードを除いて親から完全に分離します。ただし、子からの出力を表示できるようにすることだけが必要な場合は、これで問題ない可能性があります。子 (入力) と対話することもできますが、新しいウィンドウを介してのみ可能です ( を参照SendKeys)。

の使用に関してReadAll()は、ストリームからすべての入力を収集してから返すため、ストリームが閉じられるまで何も表示されないため、これはさらに悪化します。この例ReadAllでは、文字列を作成するループに を配置する理由がわかりif (!WScript.StdIn.AtEndOfStream)ません。例外を回避するには、単一で十分なはずです。

別の方法として、WMI のプロセス作成メソッドを使用することもできます。StdIn標準 I/O の処理方法は明確ではなく、特定のストリームを/ Out/として割り当てる方法もないようErrです。唯一の希望は、子が親からこれらを継承することですが、それはあなたが望むことですよね? (このコメントはアイデアと少しの調査に基づいていますが、実際のテストは行っていません。)

基本的に、スクリプト システムは、複雑なプロセス間通信/同期用には設計されていません。

注: 上記を確認するテストは、スクリプト バージョン 5.6 を使用して Windows XP SP2 で実行されました。現在の (5.8) マニュアルへの参照は、変更がないことを示唆しています。

于 2011-10-17T23:41:29.800 に答える
1

はい、ターミナル出力に関しては Exec 関数が壊れているようです。

function ConsumeStd(e) {WScript.StdOut.Write(e.StdOut.ReadAll());WScript.StdErr.Write(e.StdErr.ReadAll());}私はあなたと同様のループで呼び出す同様の関数を使用しています。EOFのチェックと行ごとの読み取りが良いか悪いかはわかりません。

于 2010-01-23T11:35:57.570 に答える