java 通过 PsExec 时程序输出丢失

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1279809/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 15:55:55  来源:igfitidea点击:

Program output lost when passed through PsExec

javapsexec

提问by mwalling

(This is a question my coworker posted elsewhere, but I thought I'd post it here to see if I could hit a different audience.)

(这是我的同事在别处发布的一个问题,但我想我会在这里发布它,看看我是否可以吸引不同的观众。)

Hello all, I'm testing the possibility of writing a small java application the will use Psexec to kick off remote jobs. In the course of testing binding the stdin and stdout of a java program to psexec I came across an odd bug.

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

My test program is a basic echo program. It starts a thread to read from stdin and then pipes the read output directly back to stdout. When run on the local machine, not from psexec, it works beautifully. Exactly as it should.

我的测试程序是一个基本的回声程序。它启动一个线程从 stdin 读取,然后将读取的输出直接通过管道返回到 stdout。当在本地机器上运行时,而不是从 psexec 上运行时,它运行得很好。正是它应该的。

However, when I call it from PsExec the first time the input is piped directly into stdout it is lost. What makes the bug really bizzare is that it is only the first time the input is piped directly into stdout that it is lost. If the input String is appended to another string it works fine. Either a String literal or a String variable. However, if the input String is sent directly to stdout it doesn't go through. The second time it is sent to stdout it goes through fine - and everytime there after.

但是,当我第一次从 PsExec 调用它时,输入直接通过管道传输到 stdout 时它丢失了。使该错误真正奇怪的是,它只是第一次将输入直接通过管道传输到 stdout 时丢失了。如果输入字符串附加到另一个字符串,它工作正常。字符串文字或字符串变量。但是,如果输入字符串直接发送到标准输出,则不会通过。第二次将它发送到标准输出时,它会正常运行 - 并且每次都在那里。

I'm at a complete loss as to what's going on here. I've tried to test for every possible bug I can think of. I'm out of ideas. Did I miss one or is this just something inside psexec?

我完全不知道这里发生了什么。我试图测试我能想到的每一个可能的错误。我没主意了。我错过了一个还是这只是 psexec 里面的东西?

Here is the code in question, it's in three classes (one of which implements an interface which is a single function interace).

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

The Main class:

主类:

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

The CReader class which is the thread that reads from stdin:

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;
            }
        }       
    }
}

The CEcho class, which is the class that pipes it back to stdout:

CEcho 类,该类是将其管道回标准输出的类:

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

And finally, here is the program output:

最后,这是程序输出:

>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

采纳答案by Niko

have you tried redirecting the output into a file ( java... >c:\output.txt )? this way you could doublecheck if everything is going into stdout and maybe just getting eaten by psexec

您是否尝试将输出重定向到文件( java... >c:\output.txt )?这样你就可以仔细检查是否一切都进入了标准输出,也许只是被 psexec 吃掉了

回答by Jon Bright

PsExec is eating the output. Next interesting thing might be whereit's eating the output. You could check this by getting a copy of Wiresharkand checking whether the output in question is traversing the network or not. If it's not, then it's being eaten on the remote side. If it is, it's being eaten locally.

PsExec 正在吃输出。下一个有趣的事情可能是它在哪里吃输出。您可以通过获取Wireshark的副本并检查有问题的输出是否遍历网络来检查这一点。如果不是,那么它正在被偏远的一侧吃掉。如果是的话,它在当地被吃掉了。

Not that I'm really sure where to go from there, but collecting more information certainly seems like a good path to be following...

并不是说我真的确定从那里去哪里,但收集更多信息当然似乎是一条很好的道路......

回答by jmanning2k

I'd guess that there is a bogus byte in there prefacing the T. According to JavaDocs, an InputStreamReader will read one or more bytes, and decode them into characters.

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

You could have an escape sequence or spurious byte in there, masquerading as a multibyte character.

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

Quick check - see if "current" is ever > 128 or < 33.

快速检查 - 查看“当前”是否曾经 > 128 或 < 33。

What if you used a CharArrayReader to get individual bytes, without any charset translation?

如果您使用 CharArrayReader 来获取单个字节而不进行任何字符集转换会怎样?

The theory is that during the first attempt to output the String using println, it's sending an escape character of some sort, eating the rest of the string. During later prints, either Java or the network pipe are handling or removing it, since it previously got that escape sequence, perhaps changing the handling in some way.

理论是,在第一次尝试使用 println 输出字符串时,它发送了某种类型的转义字符,吃掉了字符串的其余部分。在以后的打印过程中,Java 或网络管道正在处理或删除它,因为它之前获得了该转义序列,可能会以某种方式更改处理方式。

As an unrelated nit, sb.toString() returns a new String, so it's unnecessary to call "new String(sb.toString())"

作为一个无关的尼特,sb.toString() 返回一个新的String,所以没有必要调用“new String(sb.toString())”

回答by Mark

Is System.outnot configured for autoflush? After the first print try System.out.flush()and see if the first line appears without more lines being printed.

System.out不是没有配置自动刷新?第一次打印后,尝试System.out.flush()查看第一行是否出现而没有打印更多行。

(oh yeah, seriously, it is "RECEIVED", not "RECIEVED".)

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

回答by Mark

OK, I've been thinking about this over the weekend and I since you are jumping from machine to machine I wonder if maybe there is a CharSet issue? Maybe it is eating the string the first time and dealing with a different code page or character set issue? Java is 16bit characters normally and windows is either 8bit with code pages or utf-8 these days.

好的,我周末一直在考虑这个问题,因为你从一台机器跳到另一台机器,我想知道是否可能存在 CharSet 问题?也许它是第一次吃字符串并处理不同的代码页或字符集问题?Java 通常是 16 位字符,而现在 Windows 是带有代码页的 8 位字符或 utf-8。

Any chance the local and remote machines have different default character sets? If you are sending localized data over the net it might misbehave.

本地和远程机器有没有可能有不同的默认字符集?如果您通过网络发送本地化数据,它可能会出现错误行为。

回答by Joe Caffeine

What I see when running psexec is that it spawns a child window to do the work but doesnt return that program's output to it's console window. I would suggest using WMI or some form of windows process API framework to gain a level of control you appear to lack with psexec. Surely java has an equivalent to .Net's System.Diagnotics.Process class.

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

回答by Shirkrin

Maybe you could try passing a copy of input to your listeners:

也许您可以尝试将输入的副本传递给您的听众:

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

I had similar problems with listeners where a passed variable would end up empty unless I did pass an explicit copy of it.

我在侦听器中遇到了类似的问题,除非我确实传递了它的显式副本,否则传递的变量最终会为空。

回答by John Fisher

I don't necessarily have an answer, but some comments may prove helpful.

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

  • The "pass a copy" idea shouldn't matter, since your output successfully prints the string twice before the failure, then succeeds again afterward.
  • auto-flush shouldn't matter either, as you've already mentioned
  • Niko's suggestion has some merit, for diagnostic purposes. Mixed with Mark's suggestion, it makes me wonder if there aren't some invisible control characters getting involved somewhere. What if you printed the characters byte values as a diagnostic step?
  • You knowthat the value is "Test" (at least in the output you gave us). What happens if you pass "Test" directly to the failing printLn statement?
  • In situations like this, you want to gain as much information as possible. Insert breakpoints and analyze characters. Send the bytes to files and open them in hex editors. Do whatever you can to trace things as accurately and as precisely as possible.
  • Come up with weird test scenarios and try them, even if they shouldn't possibly help. You never know what good idea you might have while analyzing the results of the hopeless idea.
  • “通过副本”的想法应该无关紧要,因为您的输出在失败之前成功打印了两次字符串,然后再次成功。
  • 正如您已经提到的,自动刷新也不重要
  • 出于诊断目的,Niko 的建议有一些优点。再加上马克的建议,我不禁怀疑是不是有一些隐形控制角色被卷入了某个地方。如果您打印字符字节值作为诊断步骤会怎样?
  • 知道该值为“测试”(至少在您提供给我们的输出中)。如果将“Test”直接传递给失败的 printLn 语句会发生什么?
  • 在这种情况下,您希望获得尽可能多的信息。插入断点并分析字符。将字节发送到文件并在十六进制编辑器中打开它们。尽你所能,尽可能准确地追踪事物。
  • 想出奇怪的测试场景并尝试它们,即使它们不应该有帮助。在分析无望的想法的结果时,您永远不知道自己可能有什么好主意。

回答by jchen

Same issue here, I'm going through this post again and again these days, hoping I can find some solution. Then I decide I should give up psexec and find some alternative. So this is the thing: PAExec. Works perfect for getting command output.

同样的问题在这里,我这些天一遍又一遍地浏览这篇文章,希望我能找到一些解决方案。然后我决定我应该放弃 psexec 并找到一些替代方案。所以这就是事情:PAExec。非常适合获取命令输出。

回答by farheen89

I was having the same issue and tried multiple combinations of redirects. This is what worked:

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

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();

The Redirect.INHERIT for processBuilder.redirectInput got me the missing remote command output.

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