(これは私の同僚が他の場所に投稿した質問ですが、別の聴衆にヒットできるかどうかを確認するためにここに投稿すると思いました。)
みなさん、こんにちは。Psexecを使用してリモートジョブを開始する小さなJavaアプリケーションを作成する可能性をテストしています。Javaプログラムのstdinとstdoutをpsexecにバインドするテストの過程で、奇妙なバグに遭遇しました。
私のテストプログラムは基本的なエコープログラムです。stdinから読み取るスレッドを開始し、読み取った出力を直接stdoutにパイプで戻します。psexecからではなく、ローカルマシンで実行すると、美しく機能します。まさにそうあるべきです。
ただし、初めてPsExecから呼び出すと、入力がstdoutに直接パイプされたときに失われます。バグを本当に奇妙なものにしているのは、入力がstdoutに直接パイプされて初めて失われることです。入力文字列が別の文字列に追加されている場合は、正常に機能します。文字列リテラルまたは文字列変数のいずれか。ただし、入力文字列がstdoutに直接送信された場合、それは通過しません。2回目にstdoutに送信されると、問題なく処理されます。その後は毎回送信されます。
私はここで何が起こっているのか完全に途方に暮れています。考えられるすべてのバグをテストしようとしました。私はアイデアがありません。私は1つを逃しましたか、それともこれはpsexec内の何かですか?
これが問題のコードです。これは3つのクラスにあります(そのうちの1つは、単一の関数のインターフェースであるインターフェースを実装しています)。
メインクラス:
public class Main {
public static void main(String[] args) {
System.out.println("Starting up.");
CReader input = new CReader(new BufferedReader(
new InputStreamReader(System.in)));
CEcho echo = new CEcho();
input.addInputStreamListener(echo);
input.start();
System.out.println("Successfully started up. Awaiting input.");
}
}
stdinから読み取るスレッドであるCReaderクラス:
public class CReader extends Thread {
private ArrayList<InputStreamListener> listeners =
new ArrayList<InputStreamListener>();
private boolean exit = false;
private Reader in;
public CReader(Reader in) {
this.in = in;
}
public void addInputStreamListener(InputStreamListener listener) {
listeners.add(listener);
}
public void fireInputRecieved(String input) {
if(input.equals("quit"))
exit = true;
System.out.println("Input string has made it to fireInputRecieved: "
+ input);
for(int index = 0; index < listeners.size(); index++)
listeners.get(index).inputRecieved(input);
}
@Override
public void run() {
StringBuilder sb = new StringBuilder();
int current = 0, last = 0;
while (!exit) {
try {
current = in.read();
}
catch (IOException e) {
System.out.println("Encountered IOException.");
}
if (current == -1) {
break;
}
else if (current == (int) '\r') {
if(sb.toString().length() == 0) {
// Extra \r, don't return empty string.
continue;
}
fireInputRecieved(new String(sb.toString()));
sb = new StringBuilder();
}
else if(current == (int) '\n') {
if(sb.toString().length() == 0) {
// Extra \n, don't return empty string.
continue;
}
fireInputRecieved(new String(sb.toString()));
sb = new StringBuilder();
}
else {
System.out.println("Recieved character: " + (char)current);
sb.append((char) current);
last = current;
}
}
}
}
CEchoクラス。これは、それをstdoutにパイプで戻すクラスです。
public class CEcho implements InputStreamListener {
public void inputRecieved(String input) {
System.out.println("\n\nSTART INPUT RECIEVED");
System.out.println("The input that has been recieved is: "+input);
System.out.println("It is a String, that has been copied from a " +
"StringBuilder's toString().");
System.out.println("Outputting it cleanly to standard out: ");
System.out.println(input);
System.out.println("Outputting it cleanly to standard out again: ");
System.out.println(input);
System.out.println("Finished example outputs of input: "+input);
System.out.println("END INPUT RECIEVED\n\n");
}
}
そして最後に、プログラムの出力は次のとおりです。
> psexec \\ remotecomputer "C:\ Program Files \ Java \ jre1.6.0_05 \ bin \ java.exe" -jar "C:\ Documents and Settings \ testProram.jar" PsExecv1.96-プロセスをリモートで実行する Copyright(C)2001-2009 Mark Russinovich Sysinternals-www.sysinternals.com 起動。 正常に起動しました。入力を待っています。 テスト 受け取ったキャラクター:T 受け取ったキャラクター:e 受信した文字:s 受け取ったキャラクター:t 入力文字列がfireInputRecievedになりました:テスト 開始入力を受信しました 受信した入力は次のとおりです。テスト これは、StringBuilderのtoString()からコピーされた文字列です。 標準出力にきれいに出力します。 それを再び標準化するためにきれいに出力します: テスト 入力の完成した出力例:テスト 受信した入力の終了