I have a seemingly trivial problem: I want to start a terminal from a java process and give the terminal one or two commands.
I have a simple example code, that works perfectly on windows with CMD. But I have not been able to achieve the same exact behavior on a Linux nor a Mac OS machine.
I am aware, that the command needs to be changed, but unfortunately I have not been able to pass a string of arguments to a terminal on Mac.
Here the working code for windows:
import java.lang.ProcessBuilder.Redirect;
public class ExecTest {
public static void main(String[] args){
String cmd = "cmd /c start cmd.exe /K \"echo hello && echo bye\"";
try {
Process p = Runtime.getRuntime().exec(cmd);
p.waitFor();
} catch (Exception e) {
e.printStackTrace();
}
}
}
On lubuntu I have been able to create a terminal with this command:
lxterminal -l -e 'echo hello && echo bye && read'
But this only works if called by a terminal but not with the java process.
.
TLDR: What is the Linux and Mac equivalent of this command:
cmd /c start cmd.exe /K \"echo hello && echo bye\"
I suggest you use ProcessBuilder to benefit from easier output redirection and ability to consume it without using threads, and also pass the command as a String[] instead of flat String to be able to support the various wrapping approaches. If you prefer to stick with Runtime.exec(), it also supports String[], but the example below uses ProcessBuilder.
static int executeInTerminal(String command) throws IOException, InterruptedException {
final String[] wrappedCommand;
if (isWindows) {
wrappedCommand = new String[]{ "cmd", "/c", "start", "/wait", "cmd.exe", "/K", command };
}
else if (isLinux) {
wrappedCommand = new String[]{ "xterm", "-e", "bash", "-c", command};
}
else if (isMac) {
wrappedCommand = new String[]{"osascript",
"-e", "tell application \"Terminal\" to activate",
"-e", "tell application \"Terminal\" to do script \"" + command + ";exit\""};
}
else {
throw new RuntimeException("Unsupported OS ☹");
}
Process process = new ProcessBuilder(wrappedCommand)
.redirectErrorStream(true)
.start();
try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line); // Your superior logging approach here
}
}
return process.waitFor();
}
Tested on 3 operating systems. The 3 booleans is{Windows|Linux|Mac} are unexplained here as OS detection is another topic, so for the example I kept it simple and handled out of the method.
The ProcessBuilder is configured to redirect stderr to stdout, so that a single stream needs to be read. That stream is then read and logged, because you must consume stderr and stdout, in case the Terminal itself prints stuff (not the command you are running in the Terminal, this is about what the Terminal itself prints), if it prints too much you risk getting the process to block indefinitely, waiting for the buffer to be read. See this nice answer.
For macOS if the command you pass is always a single executable script, you could also use {"open", "-a", "Terminal", command}, but that will not work with echo hello && echo bye. Similarly you could use {"/Applications/Utilities/Terminal.app/Contents/MacOS/Terminal", command}, which would get you a second instance of the app running, but with same limitations.
Final note: you can offer a reasonable basic implementation, but you'll probably need to make it configurable to allow alternative terminal applications (especially on Linux where it varies a lot).
String[] cmd = {"echo", "hello", "&&", "echo", "hi"};
Runtime.getRuntime().exec(cmd);
There are multiple ways to do this, but this should work for you. Executables on Mac should run automatically in Terminal.
Possibly similar to: How To Run Mac OS Terminal Commands From Java (Using Runtime?)
If anything they have a few methods for running scripts in the terminal.
Related
I use terminal command "java -jar secondApp.jar" inside my java file to start a secondApp.jar.
I need secondApp.jar to run even if first app is killed.
This scenario works perfectly in windows environment. But when I test this in linux environment(Ubuntu 16.04) it seems that killing the first process kills the both processes.
This is the code I use to start the second app.
String command = "java -jar secondApp.jar"
Process process = Runtime.getRuntime().exec(command);
What am I doing wrong?
Prepare a batch file and a linux script file with the desired java command, then try this:
if (SystemUtils.IS_OS_WINDOWS) {
// run batch file
String batchFullPath = new File("C:\\myBatchFile.bat").getAbsolutePath();
Runtime.getRuntime().exec("cmd /C start " + batchFullPath);
} else if (SystemUtils.IS_OS_LINUX) {
// run linux script
String scriptFullPath = new File("~/myScriptFile.sh").getAbsolutePath();
File workingDir = new File("~");
Runtime.getRuntime().exec("/usr/bin/xterm " + scriptFullPath, null, workingDir);
} else {
throw new RuntimeException("Unsupported Operating System");
}
(Using xterm as it is fairly safe to assume every Linux machine has it installed)
The following method starts the cmd in Windows and it takes a parameter of the command which need to be run.
I have tested this method using the following commands: net users and it worked fine and it printed the users accounts. but if I run the dir command I get the following error:
java.io.IOEXception:
Cannot run program "dir": CreateProcess error=2, The system cannot find the file specified (in java.lang.ProcessBuilder)
Code :
private String commandOutPut;
public void startCommandLine(String s) throws IOException{
Runtime runtime = Runtime.getRuntime();
Process process = runtime.exec(s); // you might need the full path
InputStream is = process.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String commandOutPut;
while ((commandOutPut = br.readLine()) != null) {
this.commandOutPut = this.commandOutPut + "\n" + commandOutPut;
}
System.out.println(this.commandOutPut);
}
Well, obviously, your method does not start cmd. How did you get this notion?
The net command is a standalone command so it runs just fine, but the dir command is not standalone, it is an internal command of cmd.exe, so you cannot run it without launching cmd.exe to execute it.
To get it to work you will have to pass not dir but cmd.exe /c dir or something like that.
Don't know if this perception can help you. But, seems that "net users" are recognized as Windows command, since "Execute" dialog can run it.
But, for some reason, the "dir" command aren't. When try to run, Windows responds that command was not found.
Additionaly, I tried run Command with inline arguments too, but the arguments are simply ignored. (sorry for bad english)
My best guess is that this is because "net" is a real executable (there is a file WINDIR\System32\net.exe"), while "dir" is a builtin command of the command interpreter - it has no executable and is directly executed within cmd.exe.
Howevever you may get around this be invoking "dir" command inside the cmd process. The syntax - as per Microsoft docs - is:
cmd /c dir
There are also some related answers on the site:
How to execute cmd commands via Java
Run cmd commands through java
You can use the following code for this
import java.io.*;
public class demo
{
public static void main(String args[])
{
try
{
Process pro=Runtime.getRuntime().exec("cmd /c dir");
pro.waitFor();
BufferedReader redr=new BufferedReader(
new InputStreamReader(pro.getInputStream())
);
String ln;
while((ln = redr.readLine()) != null)
{
System.out.println(ln);
}
}
catch(Exception e) {}
System.out.println("Done");
}
}
I am experiencing a really confusing issue about sending commands to terminal via Java.
I have exactly this code:
Process p = Runtime.getRuntime().exec(new String[]{"useradd", server, "-p", pass, "-d", "/home/dakadocp/servers/" + server, "-s", "/bin/false"});
Runtime.getRuntime().exec(new String[]{"echo", server + ":" + pass, "|", "chpasswd"});
The first command is this one "useradd user -p password -d /home/ftp/test/ -s /bin/false" and the second one should be this echo username:new_password | chpasswd, the first command works without any problem and creates the user which I define by the "server" variable, but when I try to execute the second command to change users pass this command probably never happen, output is null and the password is not changed, but when I type this command directly to the terminal it works perfectly so this is just Java issue.
I think the problem is in the character "|", before I tried also some command with "|" and its behavior was the same as with this command. What I am doing wrongly ?
Thanks.
Welite.
| is a shell feature, and requires a shell to run. The trivial fix is to run a shell:
Runtime.getRuntime().exec(new String[] { "sh", "-c", "echo something | chpasswd" });
However, java is more than capable of writing to a process with needing a shell or echo. The better way is to just run chpasswd by itself and writing the string to it:
Process p = Runtime.getRuntime().exec(new String[] { "chpasswd" });
PrintWriter writer = new PrintWriter(p.getOutputStream());
writer.println("foo:bar");
writer.close();
I have a command like
cp -R Folder1/* Folder2/
or
rm -r /images/*.gif
It is not working to I try to run a sample program through Java
Runtime rt = Runtime.getRuntime();
Process proc = rt.exec(cmd);
return proc.waitFor();
What am i doing wrong?
When you run a process, Java creates three outputs, the exit code, the STDOUT, and the STDERR. A good program running an external process, will check all three.
You are just waiting for the process to terminate, and return the exit code.
An easy way to see what's happening is to 'inherit' the STDOUT/STDERR streams using a ProcessBuilder:
ProcessBuilder pbuilder = new ProcessBuilder("cp", "-R", "Folder1/*", "Folder2/");
pbuilder.inheritIO();
Process proc = pbuilder.start();
return proc.waitFor();
you will get a better idea of what went wrong.
Note also that I used separate String arguments for the command. This helps with ensuring the arguments are passed right to the underlying process.
Try like this:
List<String> cmd = new ArrayList<String>();
cmd.add("bash");
cmd.add("-c");
cmd.add(" rm -rf *.txt");
add the above list in ProcessBuilder then execute.
I am using this code:
ProcessBuilder builder = new ProcessBuilder("cmd.exe","java","invalidArg");
builder.redirectErrorStream(true);
try {
Process p = builder.start();
BufferedReader r = new BufferedReader(new InputStreamReader(p.getInputStream()),10240);
String line;
if(processIsTerminated(p)){
line = r.readLine();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Is there a way to have the cmd window open as well when I use the .start() function? Currently it runs hidden and if there is no response I don't know if my command was ran successfully or it didn't run at all.
Well you have only 2 ways of running a command under Windows :
the way you go : you have full access to the input, output and error stream of the lauched command, but it has no window and anyway it would not write anything there since your program gets all the output
ask cmd.exe to start the other command in its own window (and optionnaly wait for its end). But then as the command writes to its own window, you program has no access to the input, output or error streams of the command
You can obtain that 2nd execution way with the start [/w] command arguments command of cmd.exe, just type start /w cmd.exe /c "echo foo & pause" in a cmd window to see what happens (the & pause is only there to let you some time to read the output ...)
From java, it would be :
ProcessBuilder builder = new ProcessBuilder("cmd.exe","/c", "start", "/w",
"cmd", "/c", "java invalidArg & pause");