Find command in system path from Java on macOS

ghz 8months ago ⋅ 63 views

From Java I need to execute a user-installed command (let’s call it abc) on macOS. However when running

Runtime.getRuntime().exec(new String[] {"abc", "--argument"});

it cannot be found as Java seems to inherit a limited system $PATH (it works when calling it from the system shell). I figured out

Runtime.getRuntime().exec(new String[] {"sh", "-c", "$SHELL -c 'abc --argument'"});

works on my device (tested with zsh and fish as shells). However on a different device that has bash set as the system shell, it cannot be found. I had hoped that calling $SHELL would load the login path.

Both of these do not work:

Runtime.getRuntime().exec(cmd);
processBuilder.start();

What is the correct way to run a command in the System $PATH from Java?

Answers

When running a command from Java, it's important to ensure that the command is executed within the context of the correct environment, including the system $PATH variable. Here are a few approaches you can try:

  1. Use Absolute Path: Instead of relying on the system $PATH, you can specify the absolute path to the command in your Java code. This ensures that the command is found regardless of the environment's $PATH settings.

    Runtime.getRuntime().exec("/path/to/abc --argument");
    
  2. Modify $PATH Programmatically: You can modify the $PATH variable programmatically within your Java code before executing the command. This ensures that the command is found in the modified $PATH.

    String command = "abc --argument";
    String[] env = {"PATH=/new/path:" + System.getenv("PATH")};
    Runtime.getRuntime().exec(command, env);
    
  3. Use ProcessBuilder with Inherited Environment: ProcessBuilder provides a more flexible way to execute commands and allows you to inherit the environment of the current Java process. This ensures that the command is executed within the same environment as the Java process.

    ProcessBuilder pb = new ProcessBuilder("abc", "--argument");
    pb.inheritIO(); // Inherit the I/O streams of the Java process
    pb.start();
    
  4. Use Shell Invocation: If the command relies on shell-specific behavior (e.g., shell aliases, environment variables), you can invoke a shell and execute the command within it.

    Runtime.getRuntime().exec(new String[] {"sh", "-c", "abc --argument"});
    

    However, as you've noticed, this approach may not work consistently across different shell environments.

Choose the approach that best fits your requirements and the specific behavior of the command you're executing. If you're encountering issues with one approach, try another until you find one that works reliably across different environments.