Issue
I need to execute in java 2 commands in one terminal, each command contains a path with spaces.
For example:
CMD 1: "C:\user\with space\bat1.bat"
CMD 2: "C:\user\with space\bat2.bat"
The purpose is running CMD 2 only if CMD 1 ran successfully, in the same terminal.
I've tried to use && :
Runtime.getRuntime().exec("\"C:\\user\\with space\\bat1.bat\" && \"C:\\user\\with space\\bat2.bat\"");
But I got the following error:
'\"C:\user\with space\bat1.bat\"' is not recognized as an internal or external command,
operable program or batch file.
How can I run them both and make it recognize each command separately?
Solution
You've confused a terminal with execute. These are not the same thing. Your OS can run applications. The terminal isn't "the OS". It's an application. When you type stuff in there, you are not sending it 'to the system', you're just typing in that application. No different from me typing in this very text box in a web browser. I type a letter and it appears. I hit enter and stuff happens.
The shell does a whole boatload of 'parsing' on what you type and then uses the equivalent of Runtime.getRuntime().exec
in the language it is written in.
There are a lot of things that /bin/bash
, /bin/zsh
, /bin/fish
, windows's cmd.exe
, and all those other shells do. For some of these, it really IS the OS that does it... depending on which OS you're using. It's best to rely on absolutely none of these:
- Scour the PATH when you type a non-absolute-path executable to know which one to run.2
- Append
.exe
,.bat
,.com
, or similar. - A whole bunch of well known commands like
cd
,ls
,mkdir
, etcetera. &&
,||
and other ways to chain things.- filename substitutions, such as
whatever.exe *.txt
(the *.txt is 'substitution').1, same goes for?
. - variable substitution, such as
/bin/foo $fn
. - Redirect pipes such as
2>/dev/null
or<myfile.txt
. - splitting the command up by separating on spaces and considering the first thing the path of a command and all other things, each individually, a single argument... but then looking at quotes, using those to figure out certain spaces are not separators between parameters, and removing the actual quote characters of course.
- Replace
~
with the user's home dir. - And much, much more.
You need to do all of it yourself. That's why you should never use Runtime.getRuntime().exec
and always use ProcessBuilder
instead. You can set up redirects with that, do the argument splitting on your own. Always specify an absolute path. Don't use * or ? for anything, etcetera.
You can't write, from within java, a magical way to make 2 batch files not open up 2 black boxes. Instead, create a new batch file that runs both batch files (I think you have to do CALL bat1.bat
to avoid 3 black boxes showing up), and then run that, and you can't just run the batch file, you run cmd.exe and tell IT to run the batch file. If it's very very simple, you may be able to tell CMD to do it in one go.
Path workingDir = Paths.get(".");
List<String> batOfBatsContent = List.of(
"@ECHO OFF",
"CALL bat1.bat || GOTO :fail",
"CALL bat2.bat",
":fail");
Path megaBat = workingDir.resolve("doEverything.bat");
Files.write(megaBat, batOfBats.stream().collect(Collectors.joining("\r\n"));
ProcessBuilder pb = new ProcessBuilder();
pb.command(System.getenv("COMSPEC"), "/c", batOfBats.getAbsolutePath());
pb.start();
Or possibly:
Path workingDir = Paths.get(".");
ProcessBuilder pb = new ProcessBuilder();
pb.command(System.getenv("COMSPEC"), "/c",
"\"" +
workingDir.resolve("bat1.bat").getAbsolutePath() +
"\"&&\"" +
workingDir.resolve("bat2.bat").getAbsolutePath()
+ "\"");
pb.start();
[1] Actually, windows does do this, probably. Posix OSes (everything that isn't windows, at this point in time) definitely don't.
[2] Java tries to do this, sort of, but it's not particularly reliably. If you don't have to rely on it, don't. It's security-wise dubious in any case to do so.
Answered By - rzwitserloot