Home > Blockchain >  Java ProcessBuilder cant run ".\venv\Scripts\active" in windows
Java ProcessBuilder cant run ".\venv\Scripts\active" in windows

Time:06-27

Using Java's ProcessBuilder, I try to run a python script with activating virtual environment but i'm getting error like '.\venv\Scripts\activate" "' is not recognized as an internal or external command.

In command window cmd, I can use ".\venv\Scripts\activate" directly with no problem but in my code at below, error occured.

ProcessBuilder processBuilder = new ProcessBuilder("cmd.exe", "/c","cd C:\\Users\\onurc\\PycharmProjects\\OCR", "& .\\venv\\Scripts\\activate", "& python main.py");
    

CodePudding user response:

That command doesn't look valid to me. You may be confused about what ProcessBuilder does / how windows (or any OS) works.

When you type a command in a dosbox or terminal, you're sending it to the shell - on linux, probably /bin/bash, or zsh or whatnot. On windows, you're sending it to cmd.exe. That program (so, bash or cmd) then decodes what you typed and does all sorts of fancy things to it, and then it in turns asks the underlying kernel to actually go run that app.

Java's ProcessBuilder does not invoke cmd/bash/zsh, it is a sibling to cmd/bash/zsh - it also does some fancy things, but far fewer fancy things. On windows, even the OS (or rather, the tool) does some fancy things whereas on linux it does not.

In general, trying to even guess whether the tool you run does fancy things, and which fancy things java's ProcessBuilder does, is not a sound way to write dependable software. Especially considering the relative hardship of testing this, given that it involves invoking external stuff.

Hence, you should opt out. Of all the fancy things. Thus:

  • Always call the command with a complete and absolute path.
  • Always use the multi-string/list version, never rely on java to split on spaces.
  • Never use *, ? or any other glob. Write out each and every parameter in full.
  • Do not try to use > or & or any other 'symbol that means something specific'. An OS doesn't support any of this stuff - you just pass [A] the full path to an executable and [B] a bunch of strings that the kernel will directly pass to the process without doing any processing on it. If you want to do stuff to standard out, error out, or standard in - then use Process's methods to do this.

Those &s in your command sure look like shellisms to me.

To fix:

  • You can tell the process which directory to run in, so there is no need for a cd command.
  • You should write the absolute path to CMD.exe. You can ask the environment. You're now relying on a combination of a properly configured PATH the undefined behaviour of the JVM that does a bare minimum of fancy things (turning relative paths to absolute ones by PATH-scanning is a 'fancy thing' you should not be relying on).
  • I don't know what that & thing is, but you don't want that.
  • If activate is a script (shouldn't that end in .bat or .js or .vbs or whatnot?), run that, then run python separately. If activate sets things in the local environment table, make a script file that runs both and run the script instead.
  • Running python is not a good idea. Run C:\whatever\python.exe.

You have the right idea for activate, more or less (you're in a known dir and then write an actual path. If python.exe is guaranteeed to be in that OCR dir, run .\\python.exe, not python.

CodePudding user response:

Convert command as a single string instead of passing separate arguments to the ProcessBuilder.

ProcessBuilder processBuilder = new ProcessBuilder("cmd.exe", "/c","cd C:\\Users\\onurc\\PycharmProjects\\OCR & .\\venv\\Scripts\\activate & python main.py");

Another way could be, if all command has to run under same folder, then change ProcessBuilder working directory using directory method:

Path rootDir = Paths.get("C:\\Users\\onurc\\PycharmProjects\\OCR");
ProcessBuilder processBuilder = new ProcessBuilder("cmd", "/C", ".\\venv\\Scripts\\activate & python main.py");
processBuilder.directory(rootDir.toFile());
Process process = processBuilder.start();
process.waitFor();
  • Related