Java Process.waitFor() 和 Readline 挂起

2024-02-12

首先,这是我的代码:

import java.io.*;
import java.util.Date;

import com.banctecmtl.ca.vlp.shared.exceptions.*;

public class PowershellTest implements Runnable {

public static final String PATH_TO_SCRIPT = "C:\\Scripts\\ScriptTest.ps1";
public static final String SERVER_IP = "XX.XX.XX.XXX";
public static final String MACHINE_TO_MOD = "MachineTest";

/**
 * @param args
 * @throws OperationException 
 */
public static void main(String[] args) throws OperationException {

    new PowershellTest().run();

}

public PowershellTest(){}

@Override
public synchronized void run() {
    String input = "";
    String error = "";

    boolean isHanging = false;

    try {

        Runtime runtime = Runtime.getRuntime();
        Process proc = runtime.exec("powershell -file " + PATH_TO_SCRIPT +" "+ SERVER_IP +" "+ MACHINE_TO_MOD);
        proc.getOutputStream().close();
        InputStream inputstream = proc.getInputStream();
        InputStreamReader inputstreamreader = new InputStreamReader(inputstream);
        BufferedReader bufferedreader = new BufferedReader(inputstreamreader);

        proc.waitFor();

        String line;
        while (!isHanging && (line = bufferedreader.readLine()) != null) {
            input += (line + "\n");
            Date date = new Date();
            while(!bufferedreader.ready()){

                this.wait(1000);
                //if its been more then 1 minute since a line has been read, its hanging. 
                if(new Date().getTime() - date.getTime() >= 60000){

                    isHanging = true;
                    break;

                }
            }
        }

        inputstream.close();
        inputstream = proc.getErrorStream();
        inputstreamreader = new InputStreamReader(inputstream);
        bufferedreader = new BufferedReader(inputstreamreader);
        isHanging = false;

        while (!isHanging && (line = bufferedreader.readLine()) != null) {
            error += (line + "\n");
            Date date = new Date();
            while(!bufferedreader.ready()){

                this.wait(1000);
                //if its been more then 1 minute since a line has been read, its hanging. 
                if(new Date().getTime() - date.getTime() >= 60000){

                    isHanging = true;
                    break;

                }
            }
        }
        inputstream.close();

        proc.destroy();

    } catch (IOException e) {
        //throw new OperationException("File IO problem.", e);
    } catch (InterruptedException e) {
        //throw new OperationException("Script thread problem.",e);
    }
    System.out.println("Error : " + error + "\nInput : " + input);
}

}

我目前正在尝试运行一个 powershell 脚本,该脚本将在远程服务器上启动/停止虚拟机 (VMWARE)。该脚本通过命令行工作,此代码也是如此。问题是,我讨厌我必须使用线程(并让它等待脚本响应,如进一步解释的)来完成这样的工作。我必须这样做,因为 BufferedReader.readline() 和 proc.waitFor() 都永远挂起。

从 cmd 运行该脚本时,执行时间很长。从验证服务器身份验证到执行实际脚本,它会停滞 30 秒到 1 分钟。从我在调试中看到的情况来看,当 readline 开始从脚本接收这些延迟时,它会挂起。

我也很确定这不是内存问题,因为我在任何调试会话中都没有遇到任何 OOM 错误。

现在我明白 Process.waitFor() 要求我从错误流和常规流中刷新缓冲区才能工作,这就是我不使用它的主要原因(我需要输出来管理虚拟机特定错误、证书问题, ETC。)。

我想知道是否有人可以向我解释它为什么挂起,以及是否有一种方法可以只使用典型的 readline() 而不会让它挂得那么厉害。即使脚本应该已经结束一段时间了,它仍然挂起(我尝试使用与我在java应用程序中使用的完全相同的东西同时运行java应用程序和cmd命令,让它运行1小时,没有任何效果)。它不仅仅是被卡住了inwhile 循环中,readline() 是挂起的地方。

另外,这是一个测试版本,与最终代码相距甚远,所以请饶恕我:这应该是一个常量,这是无用的,等等。我稍后会清理代码。而且显然,我的代码中的 IP 不是 XX.XX.XX.XXX。

有关如何修复的解释或建议将不胜感激。

顺便说一句,这是我当前使用的脚本:

Add-PSSnapin vmware.vimautomation.core

Connect-VIServer -server $args[0]

Start-VM -VM "MachineTest"

如果您需要更多详细信息,我会尽力提供。

在此先感谢您的帮助!

编辑:我之前还使用要求不高的脚本测试了代码,该脚本的工作是获取文件的内容并打印它。由于无需等待即可获取信息,因此 readline() 运行良好。因此,我相当确定问题出在脚本执行的等待时间上。

另外,请原谅我的错误,英语不是我的主要语言。

在此先感谢您的帮助!

编辑2:因为我无法回答我自己的问题:

这是使用线程后我的“最终”代码:

import java.io.*;

public class PowershellTest implements Runnable {

public InputStream is;

public PowershellTest(InputStream newIs){

    this.is = newIs;
}

@Override
public synchronized void run() {
    String input = "";
    String error = "";

    try {


        InputStreamReader inputstreamreader = new InputStreamReader(is);
        BufferedReader bufferedreader = new BufferedReader(inputstreamreader);

        String line;
        while ((line = bufferedreader.readLine()) != null) {
            input += (line + "\n");

        }

        is.close();

    } catch (IOException e) {
        //throw new OperationException("File IO problem.", e);
    }
    System.out.println("Error : " + error + "\nInput : " + input);
}

}

main 只需创建并启动 2 个线程(PowerShellTest 实例),其中 1 个用于 errorStream,1 个用于 inputStream。

我相信,当我第一次对应用程序进行编码时,我犯了一个愚蠢的错误,并在一遍又一遍地修改代码时以某种方式修复了它。它仍然需要 5-6 分钟才能运行,这在某种程度上是相似的,甚至比我之前的代码长(这是合乎逻辑的,因为在我的例子中 errorStream 和 inputStream 顺序获取它们的信息)。

不管怎样,感谢您的所有回答,特别是 Miserable Variable 对线程的提示。


首先,不要打电话waitFor()直到您读完流之后。我强烈推荐你看看ProcessBuilder而不是简单地使用Runtime.exec,并自己拆分命令,而不是依赖 Java 来为您执行此操作:

ProcessBuilder pb = new ProcessBuilder("powershell", "-file", PATH_TO_SCRIPT,
        SERVER_IP, MACHINE_TO_MOD);
pb.redirectErrorStream(true); // merge stdout and stderr
Process proc = pb.start();

redirectErrorStream将错误输出合并到正常输出中,因此您只需阅读proc.getInputStream()。然后您应该能够读取该流直到 EOF,then call proc.waitFor().

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java Process.waitFor() 和 Readline 挂起 的相关文章

  • 异步填充数据集

    下面的方法用于填充数据集 如果我们以同步方式调用这个方法 它就可以正常工作 但现在我们需要以异步方式调用这个方法 那么我需要做哪些更改才能使下面的方法正常工作而不会出现任何问题 public DataSet Filldata string
  • 如何使用assertEquals 和 Epsilon 在 JUnit 中断言两个双精度数?

    不推荐使用双打的assertEquals 我发现应该使用带有Epsilon的形式 这是因为双打不可能100 严格 但无论如何我需要比较两个双打 预期结果和实际结果 但我不知道该怎么做 目前我的测试如下 Test public void te
  • 如何通过 PowerShell 获取注册表项的所有权?

    我尝试通过 PowerShell 获取注册表项的所有权 但它悄无声息地失败了 我正在使用以下代码 get the username of the current user uname System Security Principal Wi
  • 如何在 Spring 中禁用使用 @Component 注释创建 bean?

    我的项目中有一些用于重构逻辑的通用接口 它看起来大约是这样的 public interface RefactorAwareEntryPoint default boolean doRefactor if EventLogService wa
  • jQuery AJAX 调用 Java 方法

    使用 jQuery AJAX 我们可以调用特定的 JAVA 方法 例如从 Action 类 该 Java 方法返回的数据将用于填充一些 HTML 代码 请告诉我是否可以使用 jQuery 轻松完成此操作 就像在 DWR 中一样 此外 对于
  • 没有 Spring 的自定义 Prometheus 指标

    我需要为 Web 应用程序提供自定义指标 问题是我不能使用 Spring 但我必须使用 jax rs 端点 要求非常简单 想象一下 您有一个包含键值对的映射 其中键是指标名称 值是一个简单的整数 它是一个计数器 代码会是这样的 public
  • 在 junit 测试中获取 javax.lang.model.element.Element 类

    我想测试我的实用程序类 ElementUtils 但我不知道如何将类作为元素获取 在 AnnotationProcessors 中 我使用以下代码获取元素 Set
  • Eclipse Maven Spring 项目 - 错误

    I need help with an error which make me crazy I started to study Java EE and I am going through tutorial on youtube Ever
  • Hibernate 的 PersistentSet 不使用 hashCode/equals 的自定义实现

    所以我有一本实体书 public class Book private String id private String name private String description private Image coverImage pr
  • 从 Powershell 脚本安装 Python

    当以管理员身份从 PowerShell 命令行运行以下命令时 可以在 Windows 11 上成功安装 Python c temp python 3 11 4 amd64 exe quiet InstallAllUsers 0 Instal
  • 在我的 Spring Boot 示例中无法打开版本 3 中的 Swagger UI

    我在 Spring Boot 示例中打开 swagger ui 时遇到问题 当我访问 localhost 8080 swagger ui 或 localhost 8080 root api name swagger ui 时出现这种错误 S
  • Java 和 Python 可以在同一个应用程序中共存吗?

    我需要一个 Java 实例直接从 Python 实例数据存储中获取数据 我不知道这是否可能 数据存储是否透明 唯一 或者每个实例 如果它们确实可以共存 都有其单独的数据存储 总结一下 Java 应用程序如何从 Python 应用程序的数据存
  • Eclipse 选项卡宽度不变

    我浏览了一些与此相关的帖子 但它们似乎并不能帮助我解决我的问题 我有一个项目 其中 java 文件以 2 个空格的宽度缩进 我想将所有内容更改为 4 空格宽度 我尝试了 正确的缩进 选项 但当我将几行修改为 4 空格缩进时 它只是将所有内容
  • 如何使用 jUnit 将测试用例添加到套件中?

    我有 2 个测试类 都扩展了TestCase 每个类都包含一堆针对我的程序运行的单独测试 如何将这两个类 以及它们拥有的所有测试 作为同一套件的一部分执行 我正在使用 jUnit 4 8 在 jUnit4 中你有这样的东西 RunWith
  • Android:无法使用 DbHelper 和 Contract 类将数据插入 SQLite

    public class Main2Activity extends AppCompatActivity private EditText editText1 editText2 editText3 editText4 private Bu
  • 我如何在java中读取二进制数据文件

    因此 我正在为学校做一个项目 我需要读取二进制数据文件并使用它来生成角色的统计数据 例如力量和智慧 它的设置是让前 8 位组成一个统计数据 我想知道执行此操作的实际语法是什么 是不是就像读文本文件一样 这样 File file new Fi
  • 使用 svn 1.8.x、subclise 1.10 的 m2e-subclipse 连接器在哪里?

    我读到 m2e 的生产商已经停止生产 svn 1 7 以外的任何版本的 m2e 连接器 Tigris 显然已经填补了维护 m2e subclipse 连接器的空缺 Q1 我的问题是 使用 svn 1 8 x 的 eclipse 更新 url
  • 如何防止在Spring Boot单元测试中执行import.sql

    我的类路径中有一个 import sql 文件 其中包含一些 INSERT 语句 当使用 profile devel 运行我的应用程序时 它的数据被加载到 postgres 数据库中 到目前为止一切正常 当使用测试配置文件执行测试时 imp
  • 当父 powershell 进程被杀死时杀死子进程

    我的用例如下 在 Visual Studio 中 在调试时 我启动 powershell 并提供一个脚本 该脚本与几个操作一起启动项目可执行文件 如下所示 exeToStart exeParams Out Host 当我从 powershe
  • Java中super关键字的范围和使用

    为什么无法使用 super 关键字访问父类变量 使用以下代码 输出为 feline cougar c c class Feline public String type f public Feline System out print fe

随机推荐