通过PsExec时程序输出丢失

时间:2009-08-14 19:41:50

标签: java psexec

(这是我的同事发布的问题elsewhere,但我想我会在这里发帖,看看我是否能够吸引不同的观众。)

大家好, 我正在测试编写一个小型Java应用程序的可能性,它将使用Psexec启动远程作业。在测试将java程序的stdin和stdout绑定到psexec的过程中,我遇到了一个奇怪的错误。

我的测试程序是一个基本的echo程序。它启动一个线程从stdin读取,然后将读取输出直接传递回stdout。当在本地机器上运行时,而不是从psexec运行时,它运行得很漂亮。正是应该如此。

但是,当我第一次将输入直接传送到stdout时,我从PsExec调用它时会丢失。是什么让这个错误真的很棒,它只是第一次将输入直接输入stdout而丢失了。如果输入String附加到另一个字符串,它可以正常工作。 String字符串或String变量。但是,如果输入String直接发送到stdout,则它不会通过。它第二次发送到stdout它经历了很好的 - 每次都在那之后。

我完全不知道这里发生了什么。我试图测试我能想到的每一个可能的错误。我没有想法。我是否想念一个或者这只是psexec中的内容?

这是有问题的代码,它有三个类(其中一个实现了一个单一函数interace的接口)。

主要课程:

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.");
    }
}

CReader类,它是从stdin读取的线程:

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"

PsExec v1.96 - Execute processes remotely
Copyright (C) 2001-2009 Mark Russinovich
Sysinternals - www.sysinternals.com


Starting up.
Successfully started up.  Awaiting input.
Test
Recieved character: T
Recieved character: e
Recieved character: s
Recieved character: t
Input string has made it to fireInputRecieved: Test


START INPUT RECIEVED
The input that has been recieved is: Test
It is a String, that has been copied from a StringBuilder's toString().
Outputting it cleanly to standard out:

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

13 个答案:

答案 0 :(得分:4)

您是否尝试将输出重定向到文件(java ...&gt; c:\ output.txt)?这样你可以重新检查一切是否进入stdout并且可能只是被psexec吃掉

答案 1 :(得分:3)

PsExec正在吃掉输出。接下来有趣的事情可能是其中正在吃输出。您可以通过获取Wireshark的副本并检查有问题的输出是否正在遍历网络来检查这一点。如果不是,那就是在遥远的一侧吃掉它。如果是的话,就会在当地吃。

并不是说我真的确定从哪里开始,但收集更多信息肯定是一条很好的路径......

答案 2 :(得分:1)

是否为autoflush配置了System.out?第一次打印后,尝试System.out.flush()并查看第一行是否显示没有打印的行。

(哦,是的,说真的,它是“收到的”,而不是“已经收到”。)

答案 3 :(得分:1)

好的,我周末一直在考虑这个问题,因为你从机器跳到机器我想知道是否有CharSet问题?也许是第一次吃字符串并处理不同的代码页或字符集问题? Java通常是16位字符,窗口是8位代码页或者最近是utf-8。

本地和远程计算机是否有不同的默认字符集?如果您通过网络发送本地化数据,则可能会出现问题。

答案 4 :(得分:1)

我在运行psexec时看到的是它生成了一个子窗口来完成工作但是没有将该程序的输出返回到它的控制台窗口。我建议使用WMI或某种形式的Windows进程API框架来获得psexec似乎缺乏的控制级别。当然java有一个等同于.Net的System.Diagnotics.Process类。

答案 5 :(得分: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);
}

我对侦听器有类似的问题,其中传递的变量最终为空,除非我确实传递了它的显式副本。

答案 6 :(得分:1)

我不一定有答案,但有些评论可能会有所帮助。

  • “传递副本”的想法无关紧要,因为您的输出在失败之前成功打印了两次字符串,然后再次成功。
  • 自动刷新也不重要,正如您已经提到的那样
  • Niko的建议有一些优点,用于诊断目的。与马克的建议相混合,它让我想知道是否有一些看不见的控制角色涉及到某个地方。如果您将字符字节值打印为诊断步骤该怎么办?
  • 知道该值为“Test”(至少在您提供给我们的输出中)。如果将“Test”直接传递给失败的printLn语句会发生什么?
  • 在这种情况下,您希望获得尽可能多的信息。插入断点并分析字符。将字节发送到文件并在十六进制编辑器中打开它们。尽你所能尽可能准确,准确地追踪事物。
  • 想出奇怪的测试场景并尝试它们,即使它们不应该提供帮助。在分析无望想法的结果时,你永远不知道自己可能有什么好主意。

答案 7 :(得分:1)

我猜测在T前面有一个虚假字节。根据JavaDocs,InputStreamReader将读取一个或多个字节,并将它们解码为字符。

你可能有一个转义序列或虚假字节,伪装成一个多字节字符。

快速检查 - 看看“当前”是否曾经> 128或者&lt; 33。

如果使用CharArrayReader获取单个字节而没有任何字符集转换怎么办?

理论上说,在第一次尝试使用println输出String时,它会发送某种类型的转义字符,吃剩下的字符串。在以后的打印过程中,Java或网络管道正在处理或删除它,因为它之前有转义序列,可能会以某种方式改变处理。

作为一个不相关的nit,sb.toString()返回一个新的String,所以不必调用“new String(sb.toString())”

答案 8 :(得分:1)

我遇到了同样的问题并尝试了多种重定向组合。 这是有用的:

processBuilder.redirectErrorStream(true);
processBuilder.redirectOutput(Redirect.PIPE);
processBuilder.redirectInput(Redirect.INHERIT);

final Process process = processBuilder.start();

// Using Apache Commons IOUtils to get output in String
StringWriter writer = new StringWriter();
IOUtils.copy(process.getInputStream(), writer, StandardCharsets.UTF_8);
String result = writer.toString();
logger.info(result);

final int exitStatus = process.waitFor();

processBuilder.redirectInput的Redirect.INHERIT让我失去了远程命令输出。

答案 9 :(得分:0)

你是如何执行PsExec的?我怀疑这是PsExec中的一些代码,它实际上是在进行回声抑制,可能是为了保护密码。测试此假设的一种方法是更改​​此代码:

    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吗?

答案 10 :(得分:0)

我正在处理同样的问题,我想知道它是否与你没有真正的窗口会话时窗口中的cmd窗口和管道如何工作有关。生成任何新进程时会发生抑制输出。您会认为,如果您生成一个进程,stdout / stderr / stdin将从生成它的进程继承;毕竟,如果您从普通的cmd窗口生成进程并且新进程的输出通过管道传送回您自己的控制台,会发生什么。但是,如果管道继承中的某些地方出错了,比如说没有传递WINDOW.GUI对象,因为没有物理窗口,windows不会让stdin / stdout / stdin被继承。任何人都可以做一些调查或打开Windows支持票吗?

答案 11 :(得分:0)

同样的问题,这些天我一次又一次地阅读这篇文章,希望我能找到一些解决方案。然后我决定放弃psexec并找到一些替代品。所以这就是:PAExec。适用于获取命令输出。

答案 12 :(得分:0)

似乎没有简单的解决方案。我在最近的一个项目中的解决方法是使用paexec.exe产品。它在JAVA(java-8)中轻松捕获输出/错误,但在完成远程命令执行后挂起。在托管计算机上的服务器内运行时,我必须抛弃一个新的子JVM进程来运行paexec.exe并在完成时强制通过其PID终止它以释放所有资源。 如果有人有更好的解决方案,请发布。