Make sure bash.exe is in PATH as Charles Duffy said. Having bash installed without that being the case, is, after all, not much use. When that's the case and your script is in the current directory, the following is equivalent to your code:
public class Test {
public static void main(String[] args) throws Exception {
String[] command = { "bash", "test.sh" };
ProcessBuilder pb = new ProcessBuilder(command).inheritIO();
Process p = pb.start();
p.waitFor();
System.out.println(p.exitValue());
}
}
Answer from g00se on Stack Overflowbash - How to execute shell script on windows using JAVA - Stack Overflow
How to execute shell command in Java? - Stack Overflow
How do I execute Windows commands in Java? - Stack Overflow
How to invoke a Linux shell command from Java - Stack Overflow
Videos
Make sure bash.exe is in PATH as Charles Duffy said. Having bash installed without that being the case, is, after all, not much use. When that's the case and your script is in the current directory, the following is equivalent to your code:
public class Test {
public static void main(String[] args) throws Exception {
String[] command = { "bash", "test.sh" };
ProcessBuilder pb = new ProcessBuilder(command).inheritIO();
Process p = pb.start();
p.waitFor();
System.out.println(p.exitValue());
}
}
Note: You should use ProcessBuilder to launch as it provides better control over I/O streams, and the Runtime.exec call you use is deprecated. However the outcome would be the same in your case.
The issue here is that bash on Windows is expecting a Linux style pathname, and you are passing in Windows OS pathname.
Your Path environment for bash.exe is correct because you don't get IOException : Cannot run program "bash.exe": CreateProcess error=2, The system cannot find the file specified.
Thus bash.exe has run, but it reports exit code 127 which means Command not found, or PATH error. To fix you need to form valid GNU/Linux pathname to the script. If you are using WSL the C drive is mapped as "/mnt/c" so you need to run the translated path:
String[] cmd = new String[]{ "bash.exe", "/mnt/c/Users/abc/Downloads/test.sh" };
// Using "bash" above should also work if "bash.exe" is in the Path and PATHEXT contains ".EXE"
Process p = new ProcessBuilder(cmd).redirectErrorStream(true).start();
p.getInputStream().transferTo(System.out);
System.out.println(p.waitFor());
You could avoid the OS pathname translation by using a relative path to the script and run from it's directory:
File exe = new File("C:\\Users\\abc\\Downloads\\test.sh");
// Set up relative path for the script
String[] cmd = new String[]{"bash", exe.getName()};
ProcessBuilder pb = new ProcessBuilder(cmd).redirectErrorStream(true);
// Run from the directory of the script:
pb.directory(exe.getParentFile());
Process p = pb.start();
p.getInputStream().transferTo(System.out);
System.out.println(p.waitFor());
I hope this helps :)
You could use:
Runtime.getRuntime().exec("ENTER COMMAND HERE");
an example. 1. create cmd 2. write to cmd -> call a command.
try {
// Execute command
String command = "cmd /c start cmd.exe";
Process child = Runtime.getRuntime().exec(command);
// Get output stream to write from it
OutputStream out = child.getOutputStream();
out.write("cd C:/ /r/n".getBytes());
out.flush();
out.write("dir /r/n".getBytes());
out.close();
} catch (IOException e) {
}
exec does not execute a command in your shell
try
CopyProcess p = Runtime.getRuntime().exec(new String[]{"csh","-c","cat /home/narek/pk.txt"});
instead.
EDIT:: I don't have csh on my system so I used bash instead. The following worked for me
CopyProcess p = Runtime.getRuntime().exec(new String[]{"bash","-c","ls /home/XXX"});
Use ProcessBuilder to separate commands and arguments instead of spaces. This should work regardless of shell used:
Copyimport java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
public class Test {
public static void main(final String[] args) throws IOException, InterruptedException {
//Build command
List<String> commands = new ArrayList<String>();
commands.add("/bin/cat");
//Add arguments
commands.add("/home/narek/pk.txt");
System.out.println(commands);
//Run macro on target
ProcessBuilder pb = new ProcessBuilder(commands);
pb.directory(new File("/home/narek"));
pb.redirectErrorStream(true);
Process process = pb.start();
//Read output
StringBuilder out = new StringBuilder();
BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line = null, previous = null;
while ((line = br.readLine()) != null)
if (!line.equals(previous)) {
previous = line;
out.append(line).append('\n');
System.out.println(line);
}
//Check result
if (process.waitFor() == 0) {
System.out.println("Success!");
System.exit(0);
}
//Abnormal termination: Log command parameters and output and throw ExecutionException
System.err.println(commands);
System.err.println(out.toString());
System.exit(1);
}
}
Runtime.getRuntime().exec("netsh");
See Runtime Javadoc.
EDIT: A later answer by leet suggests that this process is now deprecated. However, as per the comment by DJViking, this appears not to be the case: Java 8 documentation. The method is not deprecated.
Use ProcessBuilder
ProcessBuilder pb=new ProcessBuilder(command);
pb.redirectErrorStream(true);
Process process=pb.start();
BufferedReader inStreamReader = new BufferedReader(
new InputStreamReader(process.getInputStream()));
while(inStreamReader.readLine() != null){
//do something with commandline output.
}
You won't be able to run commands that affect the current invoking shell, only to run command line bash/cmd as sub-process from Java and send them commands as follows. I would not recommend this approach:
CopyString[] cmd = new String[] { "/bin/bash" }; // "CMD.EXE"
ProcessBuilder pb = new ProcessBuilder(cmd);
Path out = Path.of(cmd[0]+"-stdout.log");
Path err = Path.of(cmd[0]+"-stderr.log");
pb.redirectOutput(out.toFile());
pb.redirectError(err.toFile());
Process p = pb.start();
String lineSep = System.lineSeparator();
try(PrintStream stdin = new PrintStream(p.getOutputStream(), true))
{
stdin.print("pwd");
stdin.print(lineSep);
stdin.print("cd ..");
stdin.print(lineSep);
stdin.print("pwd");
stdin.print(lineSep);
};
p.waitFor();
System.out.println("OUTPUT:"+Files.readString(out));
System.out.println("ERROR WAS: "+Files.readString(err));
}
This also works for CMD.EXE on Windows (with different commands). To capture the response per command you should replace use of pb.redirectOutput() with code to read pb.getInputStream() if you really need the responses per line rather than as one file.
On Windows to start a command shell from Java program, you can do it as follows:
Copyimport java.io.IOException;
public class Command {
public static void main(String[] args) {
try {
Runtime.getRuntime().exec("cmd.exe /c start");
} catch (IOException e) {
e.printStackTrace();
}
}
}
You need to use the same approach for Linux.
https://www.baeldung.com/run-shell-command-in-java
so, java uses linux, and as a way to cut down on coding projects time to completion, I was wondering, if there is a bash sdk or something similar for java that can be wrapped into a jar and used to make an executable, that way a windows computer can run it, and a linux computer can as well..
I want to be able to run code from other programming languages that utilize terminal, but... I don't expect end users to be able to set up bash or even work terminal, I could write batch scripts and have two different downloads or I could have the code ping for the OS of the system and send the user to one of the two, but if possible this would be really helpful to know going forward...
just wondering if anyone knows of a package that can be wrapped in for bash integration for your jar so the scripts run within the jvm and not called by it...
By default the normal feedback mode is used in your interaction with JShell and this mode prints command output, Declaration, Commands and Prompt. Read Introduction to jshell feedback modes for more details.
Steps to get command output:
Run
jshellinconcisefeedback mode to skip Command and Declaration details, it will still print prompt and value.$ echo 'String.format("%06d", 19)' | jshell --feedback concise jshell> String.format("%06d", 19) $1 ==> "000019"Filter 2nd row from result using
sed-echo 'String.format("%06d", 19)' | jshell --feedback concise | sed -n '2p'Extract only value and exclude other details-
echo 'String.format("%06d", 19)' | jshell --feedback concise | sed -n '2p' |sed -En 's/[^>]*>(.+)/\1/gp'
If you use System.out.println, you will not need the second sed
echo "System.out.println(\"$JAVA_HOME\");" | jshell --feedback concise | sed -n '2p'