Search code examples
javaprocess

ProcessBuilder cannot run program found in PATH


I'm trying to run npm through ProcessBuilder.start(). However, in Windows at least, whenever I try to start the process without stating the full path of the executable, the invocation fails with "CreateProcess error=2".

EDIT: the directory of npm is in the PATH environment variable.

Sample code:

public class Main {
    public static void main(String[] args) {
        // Won't work
        invokeCommand(new String[] {"npm", "--version"});

        // Will work
        invokeCommand(new String[] {"C:\\Program Files\\nodejs\\npm.cmd", "--version"});
    }

    private static void invokeCommand(String[] args) {
        System.out.println("INVOKING: " + String.join(" ", args));

        ProcessBuilder pb = new ProcessBuilder(args);

        try {
            Process p = pb.start();
            p.waitFor();

            dumpStdout(p);
        } catch (IOException e) {
            System.out.println(e.getMessage());
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        System.out.println();
    }

    private static void dumpStdout(Process p) throws IOException {
        String line;
        BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));

        while ((line = reader.readLine()) != null) {
            System.out.println("STDOUT: " + line);
        }

        reader.close();
    }
}

Why is it so? Is it because npm is a batch command? That's very inconvenient, not to say inconsistent with other programs like java or node that will run just fine without their full paths. Thank you.


Solution

  • Java does scan the Path for executables, but does not reference the PATHEXT variable. So change your launcher with the file extension of the executable - which is why it works with appending ".cmd".

    It would work with the extension {"npm.cmd", "--version"}, or without the extension if you use a terminal such as {"cmd.exe", "/c", yourcommand} as CMD scans the file extensions in the environment variable PATHEXT. Typically set to:

     PATHEXT=.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC