代码之家  ›  专栏  ›  技术社区  ›  Shiridish

执行显示控制台窗口的命令并获取进程句柄

  •  5
  • Shiridish  · 技术社区  · 8 年前

    我正在尝试从Java运行一个命令,该命令将启动一个运行几分钟的进程。我只需要触发命令并获取进程句柄,然后继续循环中的其他操作。我必须定期监视该进程是否仍处于活动状态。

    我还需要显示控制台窗口,以便为用户显示进程的输出。

    目前,我尝试了运行时类和ProcessBuilder类中的方法来运行我的命令,但它们都没有帮助我实现目标。

    示例代码:

    //Changing the directory and running Maven exec: java command on the POM file in that directory.
    
    String cmd = "cd C:/Test & mvn exec:java"; 
    String finalCmd = "cmd /c \""+ cmd +"\"";
    
    Process process = Runtime.getRuntime().exec(finalCmd);
    Thread.sleep(10);
    boolean alive = process.isAlive();
    

    变量alive的值为True,但我看不到该过程已经开始。当程序执行完成时,流程才会开始,我不知道为什么会这样。

    此外,为了显示控制台窗口,我从google发现我需要使用以下命令:

    String finalCmd = "cmd /c start cmd.exe /c \"" + cmd + "\"";
    

    然而,有了这个,进程立即启动,但我没有得到进程句柄,因为我发现活动变量显示为false。

    有人知道如何实现这一目标吗?如果不能同时执行这两项操作,我也可以,但至少我需要让流程执行开始,并在代码的后面获得句柄来监视流程状态。

    4 回复  |  直到 8 年前
        1
  •  3
  •   Deepak    8 年前

    这里发生了一些错误的事情:

    • 我们需要将命令作为字符串标记传递给exec()命令
    • 我们需要等待进程退出进程。waitFor()而不是休眠,这将阻止当前线程,因此如果您不想在另一个线程中执行此操作,或者使用ExecutorService。
    • 建议检查waitFor()的输出值,以查看我们的命令是否正确执行(值为0)或没有执行(任何其他值, 如果执行不成功,通常为正1)
    • 或者(要查看输出),我们需要重定向标准并在某个地方出错,比如说将其打印到控制台(),尽管您可以将其放在某个GUI窗口等的文件中。

    因此,至少应使用以下代码:

    Process process = Runtime.getRuntime().exec(new String[] {"cmd", "/c", "cd", "C:\\dev", "&&", "dir"});
    int outputVal = process.waitFor();
    boolean alive = process.isAlive();
    System.out.format("alive %s, outputVal: %d\n",alive, outputVal);
    

    进一步建议:

    • 使用ProcessBuilder而不是runTime。exec(),它允许更多的控制 是自JDK 1.5以来的推荐方式
    • 读取输入流

    因此,代码将如下所示:

        List<String> cmdList = Arrays.asList("cmd", "/c", "cd", "C:\\dev", "&&", "dir");
        ProcessBuilder pb = new ProcessBuilder(cmdList);
        pb.redirectErrorStream(true); //redirect STD ERR to STD OUT
        Process process = pb.start();
        try (final BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
            String line = null;
            while ((line = br.readLine()) != null) {
                  System.out.println("std-out-line: " + line);
            }
        }
        int outputVal = process.waitFor();
        System.out.format("outputVal: %d\n", outputVal);
    

    由于waitFor()是一个阻塞调用,因此可以在单独的线程中或使用executorService执行此调用。此处的示例代码:

        final StringBuffer outputSb = new StringBuffer();
        ExecutorService executorService = null;
        try {
            executorService = Executors.newSingleThreadExecutor();
            final Future<Integer> future = executorService.submit(new Callable<Integer>() {
    
                @Override
                public Integer call() throws Exception {
                    try (final BufferedReader br = new BufferedReader(
                            new InputStreamReader(process.getInputStream()))) {
                        String line = null;
                        while ((line = br.readLine()) != null) {
                            outputSb.append("std-out-line: ");
                            outputSb.append(line);
                            outputSb.append('\n');
                        }
                    }
                    int exitValue = process.waitFor();
                    System.out.format("exitValue: %d\n", exitValue);
    
                    return exitValue;
                }
            });
    
            while (!future.isDone()) {
                System.out.println("Waiting for command to finish doing something else..");
                Thread.sleep(1 * 1000);
            }
    
            int exitValue = future.get();
            System.out.println("Output: " + outputSb);
    
        } finally {
            executorService.shutdown();
        }
    
        2
  •  1
  •   Ezekiel Baniaga    8 年前

    这里有一个使用WMIC的解决方案。

    public static void main( String[] args ) throws Exception {
    
        // Vars
        Process process;
        String output;
    
        // Execution
        process = Runtime.getRuntime().exec("cmd /c wmic process call create calc.exe | findstr ProcessId");
        output = readTrimmedOutput(process.getInputStream());
        System.out.println("Output from command: " + output);
    
        // Basic string manipulation to get process id
        String str_proc_id = output.split(" = ")[1].replace(";","");
        System.out.println("ProcessId is: " + str_proc_id);
    
        // Some thread delay that you can comment/uncomment for testing if running or not
        Thread.sleep(5000);
    
        // Finding if process is still running
        process = Runtime.getRuntime().exec("cmd /c wmic process get processid | findstr " + str_proc_id);
        output = readTrimmedOutput(process.getInputStream());
    
        boolean isRunning = output.contains(str_proc_id);
        System.out.println("Is process still running? " + isRunning);
    
    }
    
    private static String readTrimmedOutput(InputStream is) throws Exception {
        BufferedReader breader = new BufferedReader(new InputStreamReader(is));
        String line = breader.readLine();
        return line != null ? line.trim() : "";
    }
    

    样本输出

    Output from command: ProcessId = 6480;
    ProcessId is: 6480
    Is process still running? true
    

    要显示cmd控制台,请将某些行更改为:

    // Execution
    String your_command = "cmd.exe /c \"dir\"";
    process = Runtime.getRuntime().exec("cmd /c wmic process call create \"" + your_command + "\" | findstr ProcessId");
    

    参考文献:

    https://msdn.microsoft.com/en-us/library/aa394531(v=vs.85).aspx

    https://www.computerhope.com/wmic.htm

        3
  •  0
  •   parsa    8 年前

    由于我不太了解您真正需要什么,我带来了一个从java类(例如类a)打开cmd并启动另一个java类(类B)的进程的综合示例,并从类B执行一些操作,同时类B通知类a它是否正在处理。所以,整个过程就是从类A启动的命令promt中执行类B,并将信息从类B发送到A,通知它仍在运行。

    在我的例子中 Main class 作为A级和 myProcess class 为B类。 正如您在下面的代码中所看到的 主要类别 正在打开cmd并正在执行 myProcess类 然后 myProcess类 正在通过中创建的套接字发送有关进程的信息 主要类别

    //imports
    import java.io.BufferedReader;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.io.OutputStreamWriter;
    import java.net.ServerSocket;
    import java.net.Socket;
    //class
    public class Main 
    {
      //fields
    
      //methods
    
    
        public static void main(String[] args) throws Exception
        {
    
            Runtime run = Runtime.getRuntime();
            String new_dir = "C:\\Users\\Parsa\\Desktop\\New folder (2)";//imagine the directory of myProcess.class is in this folder
            startServer();
            run.exec("cmd.exe /c cd \""+new_dir+"\" & start cmd.exe /k \"java myProcess\"");
    
        }
    
    
        public static void startServer() 
        {
            Thread myThread = new Thread() 
            {
                @Override
                public void run() 
                {
                    ServerSocket ss;// creating an open port for receiving data from network
                    try {
                        ss = new ServerSocket(60010);//open port number 60010--> it can really be anything that is empty
    
                        Socket s = ss.accept();//Listens for a connection to be made to this socket and accepts it
    
                        BufferedReader in = new BufferedReader(
                                new InputStreamReader(s.getInputStream()));//get the inputstream and change it to a buffered reader in order to get a string from remote network
                        String line = null;
                        while ((line = in.readLine()) != null) //read the input
                        {
                            System.out.println(line);
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            };
    
            myThread.start();
    
    
        }
    
    }
    

    myProcess类:

    另外,您需要通过命令提示符手动编译myProcess类并执行myProcess。来自主类的类文件

    myProcess类是

    import java.io.BufferedWriter;
    import java.io.IOException;
    import java.io.OutputStream;
    import java.io.OutputStreamWriter;
    import java.net.Socket;
    import java.net.URL;
    import java.net.URLConnection;
    import java.net.UnknownHostException;
    import java.util.Timer;
    import java.util.TimerTask;
    
    public class myProcess extends Thread
    {
      //field
    
      //methods
        public static void main(String[] args) throws Exception
        {
            System.out.println("myProcess has started");
            startSender();
    
        }
    
    
        public static void startSender() 
        {
            Thread myThread = new Thread() 
            {
                @Override
                public void run() 
                {
                    try 
                    {
                        Socket s = new Socket("localhost", 60010);
                        BufferedWriter out = new BufferedWriter(
                                new OutputStreamWriter(s.getOutputStream()));
    
                        for(int i = 0 ; i<10 ; i++)
                        {
                            out.write("Process in running");
                            out.newLine();
                            out.flush();
    
                            Thread.sleep(200);
                        }
                        out.close();
                        //do whatever here
                        System.out.println("myProcess output");
                    } 
                    catch (Exception e) 
                    {
                        e.printStackTrace();
                    } 
                }
            };
            myThread.start();
    
    
            if(!myThread.isAlive())
            {
                System.out.println("myProcess has finished");
            }
        }
    }
    

    因为我不太明白你想要什么,这可能不是你想要的,但是。。。如果您操作代码,它肯定会对您有所帮助。

        4
  •  0
  •   Gal Nitzan    8 年前

    我认为您需要将应用程序作为进程而不是CMD启动,然后启动CMD的子进程。它与Linux中的相同。

    您启动的CMD是alive=true,但当您从该CMD启动java时,它是另一个进程,是CMD的子进程,但它不会返回预期的结果。

    HTH, 女孩

    附:你可能想看看 https://commons.apache.org/proper/commons-exec/ 在我看来,这在功能上优于Java。