You start a new process with Runtime.exec(command). Each process has a working directory. This is normally the directory in which the parent process was started, but you can change the directory in which your process is started.
I would recommend to use ProcessBuilder
ProcessBuilder pb = new ProcessBuilder("ls");
pb.inheritIO();
pb.directory(new File("bin"));
pb.start();
If you want to run multiple commands in a shell it would be better to create a temporary shell script and run this.
public void executeCommands() throws IOException {
File tempScript = createTempScript();
try {
ProcessBuilder pb = new ProcessBuilder("bash", tempScript.toString());
pb.inheritIO();
Process process = pb.start();
process.waitFor();
} finally {
tempScript.delete();
}
}
public File createTempScript() throws IOException {
File tempScript = File.createTempFile("script", null);
Writer streamWriter = new OutputStreamWriter(new FileOutputStream(
tempScript));
PrintWriter printWriter = new PrintWriter(streamWriter);
printWriter.println("#!/bin/bash");
printWriter.println("cd bin");
printWriter.println("ls");
printWriter.close();
return tempScript;
}
Of course you can also use any other script on your system. Generating a script at runtime makes sometimes sense, e.g. if the commands that are executed have to change. But you should first try to create one script that you can call with arguments instead of generating it dynamically at runtime.
It might also be reasonable to use a template engine like velocity if the script generation is complex.
EDIT
You should also consider to hide the complexity of the process builder behind a simple interface.
Separate what you need (the interface) from how it is done (the implementation).
public interface FileUtils {
public String[] listFiles(String dirpath);
}
You can then provide implementations that use the process builder or maybe native methods to do the job and you can provide different implementations for different environments like linux or windows.
Finally such an interface is also easier to mock in unit tests.
Answer from Renรฉ Link on Stack OverflowYou start a new process with Runtime.exec(command). Each process has a working directory. This is normally the directory in which the parent process was started, but you can change the directory in which your process is started.
I would recommend to use ProcessBuilder
ProcessBuilder pb = new ProcessBuilder("ls");
pb.inheritIO();
pb.directory(new File("bin"));
pb.start();
If you want to run multiple commands in a shell it would be better to create a temporary shell script and run this.
public void executeCommands() throws IOException {
File tempScript = createTempScript();
try {
ProcessBuilder pb = new ProcessBuilder("bash", tempScript.toString());
pb.inheritIO();
Process process = pb.start();
process.waitFor();
} finally {
tempScript.delete();
}
}
public File createTempScript() throws IOException {
File tempScript = File.createTempFile("script", null);
Writer streamWriter = new OutputStreamWriter(new FileOutputStream(
tempScript));
PrintWriter printWriter = new PrintWriter(streamWriter);
printWriter.println("#!/bin/bash");
printWriter.println("cd bin");
printWriter.println("ls");
printWriter.close();
return tempScript;
}
Of course you can also use any other script on your system. Generating a script at runtime makes sometimes sense, e.g. if the commands that are executed have to change. But you should first try to create one script that you can call with arguments instead of generating it dynamically at runtime.
It might also be reasonable to use a template engine like velocity if the script generation is complex.
EDIT
You should also consider to hide the complexity of the process builder behind a simple interface.
Separate what you need (the interface) from how it is done (the implementation).
public interface FileUtils {
public String[] listFiles(String dirpath);
}
You can then provide implementations that use the process builder or maybe native methods to do the job and you can provide different implementations for different environments like linux or windows.
Finally such an interface is also easier to mock in unit tests.
You can form one complex bash command that does everything: "ls; cd bin; ls". To make this work you need to explicitly invoke bash. This approach should give you all the power of the bash command line (quote handling, $ expansion, pipes, etc.).
/**
* Execute a bash command. We can handle complex bash commands including
* multiple executions (; | && ||), quotes, expansions ($), escapes (\), e.g.:
* "cd /abc/def; mv ghi 'older ghi '$(whoami)"
* @param command
* @return true if bash got started, but your command may have failed.
*/
public static boolean executeBashCommand(String command) {
boolean success = false;
System.out.println("Executing BASH command:\n " + command);
Runtime r = Runtime.getRuntime();
// Use bash -c so we can handle things like multi commands separated by ; and
// things like quotes, $, |, and \. My tests show that command comes as
// one argument to bash, so we do not need to quote it to make it one thing.
// Also, exec may object if it does not have an executable file as the first thing,
// so having bash here makes it happy provided bash is installed and in path.
String[] commands = {"bash", "-c", command};
try {
Process p = r.exec(commands);
p.waitFor();
BufferedReader b = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line = "";
while ((line = b.readLine()) != null) {
System.out.println(line);
}
b.close();
success = true;
} catch (Exception e) {
System.err.println("Failed to execute bash with command: " + command);
e.printStackTrace();
}
return success;
}
directory - How to run Java program in Bash script and give it one argument? - Unix & Linux Stack Exchange
Running a bash shell script in java - Stack Overflow
linux - Execute bash command within java program - Stack Overflow
Can't run java in bash script
Videos
You should use the returned Process to get the result.
Runtime#exec executes the command as a separate process and returns an object of type Process. You should call Process#waitFor so that your program waits until the new process finishes. Then, you can invoke Process.html#getOutputStream() on the returned Process object to inspect the output of the executed command.
An alternative way of creating a process is to use ProcessBuilder.
Process p = new ProcessBuilder("myCommand", "myArg").start();
With a ProcessBuilder, you list the arguments of the command as separate arguments.
See Difference between ProcessBuilder and Runtime.exec() and ProcessBuilder vs Runtime.exec() to learn more about the differences between Runtime#exec and ProcessBuilder#start.
When you execute a script from Java it spawns a new shell where the PATH environment variable is not set.
Setting the PATH env variable using the below code should run your script.
String[] env = {"PATH=/bin:/usr/bin/"};
String cmd = "you complete shell command"; //e.g test.sh -dparam1 -oout.txt
Process process = Runtime.getRuntime().exec(cmd, env);
To understand this, you first need to understand how you would run that command at a shell prompt.
$ sh -c "java -jar M1_MIAGE_PDL_VIZ_GROUPE3.jar menu"
Note where the double quotes are. The first argument is -c. The second argument is the stuff inside the quotes; i.e. java -jar M1_MIAGE_PDL_VIZ_GROUPE3.jar menu
Now we translate that into Java:
Process p = new ProcessBuilder(
"/bin/sh",
"-c",
"java -jar M1_MIAGE_PDL_VIZ_GROUPE3.jar menu").start();
Having said that, the above doesn't actually achieve anything. Certainly, it doesn't open a fresh console window to display the console output etcetera. Unlike Windows "CMD.exe", UNIX / Linux shells do not provide console / terminal functionality. For that you need to use a "terminal" application.
For example, if you are using GNOME
Process p = new ProcessBuilder(
"gnome-terminal",
"-e",
"java -jar M1_MIAGE_PDL_VIZ_GROUPE3.jar menu").start();
will (probably) do what you are trying to do.
I think the easiest way to do this would be to create a shell script (.sh extension) and then you can easily run that from within the Java program. There is a good answer on a previous question on how to run shell scripts within Java here.
To create a shell script you can use any text editor and create a file with the extension .sh and just enter the lines as you would in the bash terminal.
I was trying to re-use one of my old scripts for trimmomatic but I was getting error "java: command not found".
I realised that it will work if the java command is first. It stops if there is anything above it, e.g
This works:
#!/bin/bash java --version PATH=/media/msz/Arabidopsis_project/scripts $ ./test.sh openjdk 11.0.11 2021-04-20 OpenJDK Runtime Environment (build 11.0.11+9-Ubuntu-0ubuntu2.18.04) OpenJDK 64-Bit Server VM (build 11.0.11+9-Ubuntu-0ubuntu2.18.04, mixed mode, sharing)
This won't :
#!/bin/bash PATH=/media/msz/Arabidopsis_project/scripts java --version $ ./test.sh ./test.sh: line 3: java: command not found
Anyone have any idea what is going on?
Thanks for your help!
If the problem is that running the script doesn't show a terminal window, the solution depends on the window manager you're using. Gnome as well as KDE allow to edit properties of desktop shortcuts on a right-click and setting a option like "run in terminal" there.
Another way is to edit the desktop configuration file manually: you can find them in the Desktop subdirectory of your home dir. Just add a line with Terminal=true to the desktop configuration file that should run the server.
Why are you using echo? That just prints the rest of the line as a sequence of literals to the terminal.