我有两个程序:

  • 首先,它使用Console对象读取和写入数据
  • 第二,应该首先使用一些动态计算的参数
  • 运行

    第二个程序代码如下所示:
    String[] arguments = {
        "cmd", "/c",
        "java", "-cp", classPath
        lauchClass,
        // Arguments for first program
    }
    ProcessBuilder pb = new ProcessBuilder(arguments);
    pb.environment().putAll(System.getenv());
    pb.directory(workDir);
    pb.inheritIO();
    
    Process process = pb.start();
    process.waitFor();
    

    当第一个程序从第二个开始时,System.console()为null,并且在NPE上失败。

    因此,问题是:有什么方法可以使用System.console()运行另一个进程?

    最佳答案

    答案很简单:如果从诸如Eclipse或IntelliJ IDEA之类的IDE运行启动器,则可能首先没有设置System.console(),因此子进程无法继承任何内容。只需尝试从启动器向System.console()写入内容,它也会因相同的错误而失败。但是,如果您从Cmd.exe或Git Bash之类的交互式控制台启动启动器,则启动器和通过ProcessBuilder启动的进程都可以写入System.console()。您的启动器甚至不需要"cmd", "/c",无论有没有这些参数,它都可以工作。

    package de.scrum_master.stackoverflow;
    
    import java.io.File;
    import java.io.IOException;
    
    public class Launcher {
      public static void main(String[] args) throws IOException, InterruptedException {
        String classPath = "out/production/SO_ExternalProcessSystemConsole";
        String launchClass = "de.scrum_master.stackoverflow.MyApp";
        File workDir = new File(".");
        System.console().printf("Hi, I am the launcher app!%n");
    
        String[] arguments = new String[] {
    //      "cmd", "/c",
          "java", "-cp", classPath,
          launchClass
        };
        ProcessBuilder pb = new ProcessBuilder(arguments);
        pb.environment().putAll(System.getenv());
        pb.directory(workDir);
        pb.inheritIO();
    
        Process process = pb.start();
        process.waitFor();
      }
    }
    

    package de.scrum_master.stackoverflow;
    
    public class MyApp {
      public static void main(String[] args) {
        System.console().printf("Hi, I am an externally started app!%n");
      }
    }
    

    从IntelliJ IDEA启动时的控制台日志:

    Exception in thread "main" java.lang.NullPointerException
        at de.scrum_master.stackoverflow.Launcher.main(Launcher.java:11)
        (...)
    

    从cmd.exe启动时的控制台日志:

    Hi, I am the launcher app!
    Hi, I am an externally started app!
    

    随时提出任何后续问题。

    更新:如果您不介意外部程序在其自己的交互式控制台中而不是在IDE控制台中运行,则可以为此目的使用Windows命令start,然后使用cmd /c(外部程序结束后立即关闭Windows) )或cmd /k(窗口保持打开状态,您可以检查结果):

    package de.scrum_master.stackoverflow;
    
    import java.io.File;
    import java.io.IOException;
    
    public class Launcher {
      public static void main(String[] args) throws IOException, InterruptedException {
        String classPath = "out/production/SO_ExternalProcessSystemConsole";
        String launchClass = "de.scrum_master.stackoverflow.MyApp";
        String[] arguments = new String[] {
          "cmd", "/c", "start",
          "cmd", "/k", "java", "-cp", classPath, launchClass
        };
        ProcessBuilder pb = new ProcessBuilder(arguments);
        Process process = pb.start();
        process.waitFor();
      }
    }
    

    但是,如果您想从该控制台读/写该控制台,您将回到第一个正方形。您问为什么不能将System.console()继承到子进程。嗯,这是因为Eclipse和IntelliJ从IDE内部启动Java程序的方式,所以它是null(有关背景信息,请参见[here])。但是正如Magnus所说,您仍然有System.{out|in}并可以按以下方式使用它们:

    package de.scrum_master.stackoverflow;
    
    import java.io.File;
    import java.io.IOException;
    import java.util.Scanner;
    
    public class Launcher {
      public static void main(String[] args) throws IOException, InterruptedException {
        String classPath = "out/production/SO_ExternalProcessSystemConsole";
        String launchClass = "de.scrum_master.stackoverflow.MyApp";
        File workDir = new File(".");
        System.out.println("Hi, I am the launcher app!");
    
        String[] arguments = new String[] { "cmd", "/c", "java", "-cp", classPath, launchClass };
        ProcessBuilder pb = new ProcessBuilder(arguments);
        pb.environment().putAll(System.getenv());
        pb.directory(workDir);
        pb.inheritIO();
        Process process = pb.start();
        process.waitFor();
    
        System.out.print("What is your favourite city? ");
        Scanner scanner = new Scanner(System.in);
        String city = scanner.nextLine();
        System.out.println("I guess that " + city + " is a nice place.");
      }
    }
    

    package de.scrum_master.stackoverflow;
    
    import java.util.Scanner;
    
    public class MyApp {
      public static void main(String[] args) {
        System.out.println("----------------------------------------");
        System.out.println("Hi, I am an externally started app.");
        System.out.print("Please enter your name: ");
        Scanner scanner = new Scanner(System.in);
        String name = scanner.nextLine();
        System.out.println("Hello " + name + "!");
        System.out.println("----------------------------------------");
      }
    }
    

    Hi, I am the launcher app!
    ----------------------------------------
    Hi, I am an externally started app.
    Please enter your name: Alexander
    Hello Alexander!
    ----------------------------------------
    What is your favourite city? Berlin
    I guess that Berlin is a nice place.
    

    09-10 05:16
    查看更多