15

(これは私の同僚が他の場所に投稿した質問ですが、別の聴衆にヒットできるかどうかを確認するためにここに投稿すると思いました。)

みなさん、こんにちは。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()からコピーされた文字列です。
標準出力にきれいに出力します。

それを再び標準化するためにきれいに出力します:
テスト
入力の完成した出力例:テスト
受信した入力の終了
4

13 に答える 13

4

出力をファイルにリダイレクトしようとしましたか(java。。。> c:\ output.txt)?このようにして、すべてがstdoutに入り、psexecに食べられてしまうかどうかを再確認できます。

于 2009-08-24T22:52:31.523 に答える
3

PsExec は出力を消費しています。次に興味深いのは、出力を食べているところかもしれません。これは、 Wiresharkのコピーを取得し、問題の出力がネットワークを通過しているかどうかを確認することで確認できます。そうでない場合は、リモート側で食べられています。もしそうなら、それは地元で食べられています。

そこからどこへ行くべきか本当に確信があるわけではありませんが、より多くの情報を収集することは確かに良い道のようです...

于 2009-08-28T13:51:34.470 に答える
1

ここでも同じ問題があります。解決策を見つけられることを期待して、最近この投稿を何度も読んでいます。次に、psexec をあきらめて、代替手段を探すことにしました。これが問題です:PAExec。コマンド出力を取得するのに最適です。

于 2014-09-17T04:30:43.947 に答える
1

OK、私は週末にこれについて考えていました.あなたがマシンからマシンへとジャンプしているので、CharSetの問題があるのではないかと思います. おそらく、初めて文字列を食べて、別のコード ページまたは文字セットの問題に対処しているのでしょうか? Java は通常 16 ビット文字であり、Windows は最近ではコード ページ付きの 8 ビットまたは utf-8 です。

ローカル マシンとリモート マシンのデフォルトの文字セットが異なる可能性はありますか? ネット経由でローカライズされたデータを送信している場合、誤動作する可能性があります。

于 2009-08-24T18:12:07.830 に答える
1

psexec を実行すると、作業を行うために子ウィンドウが生成されますが、そのプログラムの出力がコンソール ウィンドウに返されません。WMI または Windows プロセス API フレームワークの何らかの形式を使用して、psexec に欠けているように見えるレベルの制御を取得することをお勧めします。確かに、Java には .Net の System.Diagnotics.Process クラスに相当するものがあります。

于 2009-08-26T06:04:36.750 に答える
1

System.out自動フラッシュ用に構成されていませんか? 最初の印刷後、最初System.out.flush()の行が表示され、それ以上行が印刷されないかどうかを確認してください。

(そうそう、まじめな話、「RECEIVED」ではなく「RECEIVED」です。)

于 2009-08-19T18:45:54.337 に答える
1

T の前に偽のバイトがあると思います。JavaDocs によると、InputStreamReader は 1 つ以上のバイトを読み取り、それらを文字にデコードします。

マルチバイト文字になりすましたエスケープシーケンスまたは偽のバイトがそこにある可能性があります。

簡単なチェック - 「現在」が 128 より大きいか 33 より小さいかどうかを確認します。

charset 変換を行わずに CharArrayReader を使用して個々のバイトを取得するとどうなるでしょうか?

理論は、println を使用して文字列を出力しようとする最初の試行中に、何らかのエスケープ文字を送信して、残りの文字列を消費するというものです。後の印刷中に、Java またはネットワーク パイプのいずれかがそれを処理または削除しています。これは、以前にそのエスケープ シーケンスを取得したためであり、おそらく何らかの方法で処理が変更されています。

無関係なニットとして、sb.toString() は新しい String を返すため、「new String(sb.toString())」を呼び出す必要はありません。

于 2009-08-28T15:54:38.510 に答える
1

入力のコピーをリスナーに渡してみてください:

 public void fireInputRecieved(String input) {

    if(input.equals("quit"))
    exit = true;

    String inputCopy = new String(input);
    System.out.println("Input string has made it to fireInputRecieved: "
        + input);



    for(int index = 0; index < listeners.size(); index++)
        listeners.get(index).inputRecieved(inputCopy);
}

明示的なコピーを渡さない限り、渡された変数が空になるというリスナーにも同様の問題がありました。

于 2009-08-26T08:59:03.700 に答える
1

必ずしも答えがあるわけではありませんが、いくつかのコメントが役立つ場合があります。

  • 出力が失敗する前に文字列を 2 回正常に出力し、その後再び成功するため、「コピーを渡す」という考えは重要ではありません。
  • すでに述べたように、自動フラッシュも重要ではありません
  • ニコの提案には、診断目的のために、いくつかのメリットがあります。マークの提案と混ざって、目に見えない制御文字がどこかに関与しているのではないかと思います. 診断ステップとして文字のバイト値を出力した場合はどうなるでしょうか?
  • 値が "Test" であることはわかっています (少なくとも、提供された出力では)。失敗した printLn ステートメントに直接 "Test" を渡すとどうなりますか?
  • このような状況では、できるだけ多くの情報を取得したいと考えています。ブレークポイントを挿入して文字を分析します。バイトをファイルに送信し、16 進エディターで開きます。物事をできるだけ正確かつ正確に追跡するために、できる限りのことをしてください。
  • 奇妙なテスト シナリオを考え出し、それが役に立たない場合でも試してみてください。絶望的なアイデアの結果を分析しているときに、どんな良いアイデアが浮かぶかは決してわかりません。
于 2009-08-27T03:11:44.883 に答える
0

どのように PsExec を実行していますか? 私の疑いでは、これはおそらくパスワードを保護する目的で、実際にエコー抑制を行っている PsExec 内のコードであるということです。この仮説を検証する 1 つの方法は、次のコードを変更することです。

    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("Outputting it cleanly to standard out: ");
    System.out.print(' ');
    System.out.println(input);
    System.out.println("Outputting it cleanly to standard out again: ");
    System.out.println(input);

...それにより、出力が次のようになります(私が正しい場合):

Outputting it cleanly to standard out:
 Test
Outputting it cleanly to standard out again:
Test
Finished example outputs of input: Test

特に、明らかに抑制されている行がTest、リモート システムに送信したばかりのテキストだけで構成される最初の行であることは注目に値します。これは、PsExec が、独自の出力を生成するだけでなく、入力をエコーし​​ているリモート システムを抑制しようとしているように思えます。

おそらくリモートマシンのユーザーのパスワードTestですか?PsExec の-pパラメータを使用していますか? 指定してい-iますか?

于 2009-08-28T10:50:51.087 に答える