在 Java 中使用 ProcessBuilder 的问题

Posted

技术标签:

【中文标题】在 Java 中使用 ProcessBuilder 的问题【英文标题】:Problems using ProcessBuilder with Java 【发布时间】:2021-08-09 23:21:36 【问题描述】:

我试图让几个 .bat 文件从一个文件夹运行,得到一个错误,只有第一个 bat 运行,其他的被忽略。

 final String direct = "FOLDER";

        File[] archives ;
        File diretorio = new File(direct);
        archives = diretorio.listFiles();

        try 
            for (int i = 0; i < archives.length; i++) 
                ProcessBuilder pb = new ProcessBuilder(archives[i].getPath());
                Process p = pb.start();
                
             //Only for printing the commands 
                BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
                String trace = null;
                while ((trace = reader.readLine()) != null) 
                    System.out.println(trace);
                
            

         catch (IOException e) 
            e.printStackTrace();
        

【问题讨论】:

您能否编辑您的帖子并包括您的故障排除步骤?例如,reader.readLine() 是否永远不会返回 null,这就是为什么您的程序永远不会尝试启动第二个? 【参考方案1】:
    确保您实际处理的是.bat 批处理文件。 请务必关闭阅读器,以便释放资源。 完成后销毁进程以释放资源。

这应该可行:

File[] archives;
File diretorio = new File(direct);
archives = diretorio.listFiles();

try 
    for (int i = 0; i < archives.length; i++) 
        // Is it a .bat file
        String fileName = archives[i].getName();
        if (!fileName.contains(".")
                || !fileName.substring(fileName.lastIndexOf(".")).equalsIgnoreCase(".bat")) 
            // No...
            continue;
        
        if (archives[i].isFile() && archives[i].canExecute()) 
            ProcessBuilder pb = new ProcessBuilder("cmd.exe", "/C", archives[i].getPath());
            Process p = pb.start();

            //Only for printing the commands
            // 'Try With Resources' used here to auto-close reader.
            try (java.io.BufferedReader reader = new BufferedReader(new java.io.InputStreamReader(p.getInputStream()))) 
                String trace = null;
                while ((trace = reader.readLine()) != null) 
                    System.out.println(trace);
                
            
            p.waitFor();
            p.destroy(); // In case of abnormal process termination though 'waitFor()'.
        
    

catch (IOException | InterruptedException e) 
    e.printStackTrace();

更新:

代码已根据有效的 cmets 进行了更新。 Process#waitFor() 在此更新后的代码中使用。

在 Windows 操作系统中,批处理文件通过 Windows 命令处理器(命令提示符)运行,但如果在 Windows 文件资源管理器中双击批处理文件,则可以通过 Windows 文件关联运行。

如果关联可用,则以前版本的代码应该可以工作

ProcessBuilder pb = new ProcessBuilder(archives[i].getPath());

但有时 .bat 文件的关联可能会无意中与文本编辑器关联,或者注册表中的设置已被篡改。一旦您使用 Open With 对话框或默认程序为 .bat 文件设置了文件关联,显然无法使用用户界面恢复到默认设置。解决此问题的唯一方法是更改​​注册表中的设置。您可以按照以下步骤进行操作:

在继续之前,请创建一个系统还原点作为安全措施。错误修改 Windows 注册表可能会导致严重问题。

    打开记事本或您喜欢的文本编辑器。 复制以下行并将其粘贴到记事本中:
Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\.bat]
@="batfile"

[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\batfile\shell]
@=-

[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\batfile\shell\open\command]
@="\"%1\" %*"

[-HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.bat]
    使用具有 .reg 扩展名的任何名称保存文件,例如 BAT_File_Association.reg 双击BAT_File_Association.reg应用配置 进入注册表。在提示输入时单击 Yes 确认。

您现在已修复批处理文件关联设置。批处理文件现在应该在双击时正确执行。你可以阅读更多关于这个here的信息。

无论如何,上面更新的代码现在将通过命令处理器 (cmd.exe) 运行批处理文件,并且再次...应该可以工作。因为我的系统有 .bat 文件关联,所以任何一种方式都适合我。

为确保您的批处理文件没有问题,请在每个 .bat 文件的顶部添加一个回显,例如:

@echo:Batch File 1
... the rest of batch file code ...

随着每个 bat 文件的运行,您应该会在控制台窗口中看到:

Batch File 1
Batch File 2
Batch File 3
etc

【讨论】:

使用 waitFor(timeout) 比使用 destroy() 更好 - 这允许进程正常结束并在杀死它之前检查退出状态。 @that-other-guy 我试图在没有时间的情况下运行,但它只运行第一个(出于某种原因,不要全部运行) @DevilsHnd 我尝试运行此代码,它识别 .bat 文件但不会运行任何【参考方案2】:

@DevilsHnd 代码效果很好! 问题出在带有“暂停”命令的 .bat 文件上。

【讨论】:

是的...这样就可以了。顺便说一句,这应该是评论而不是答案。 ://

以上是关于在 Java 中使用 ProcessBuilder 的问题的主要内容,如果未能解决你的问题,请参考以下文章

在 Java 中使用 ProcessBuilder 读取输出 git-bash

Java ProcessBuilder:结果进程挂起

Java使用ProcessBuilder类调用外部程序

为啥我在 ProcessBuilder --Java 中收到 IllegalThreadStateException 错误?

Java 沙盒和 ProcessBuilder

无法从 Java 进程(Runtime.getRuntime().exec() 或 ProcessBuilder)读取 InputStream