I'm trying to create an executable .jar that re-opens itself in Mac's Terminal console. (for the sake of having a user interface to enter commands into the program)
// if program is not open in Terminal:
Runtime.getRuntime().exec("java -jar \"" + path + "\" isInConsole");
System.exit(0);
This code executes the command successfully but seamlessly so I don't get the console UI. How can I make it open a visible Terminal window and execute a command in it?
EDIT: I managed to open the Terminal, but still need to figure out how to run the java -jar ... command in it.
This works:
String arg = "cd /Users/potato/Desktop";
Runtime.getRuntime().exec("open -a Terminal --args " + arg);
But this doesn't work:
String arg = "java -jar /Users/potato/Desktop/test.jar isInConsole";
Runtime.getRuntime().exec("open -a Terminal --args " + arg);
For creating processes, class Runtime has been superseded by class ProcessBuilder. A very old but still relevant article regarding class Runtime (because it was published before addition of class ProcessBuilder to JDK) is When runtime.exec() won't and is also relevant for class ProcessBuilder.
As stated in the article, method exec() is not a "shell" and so does not parse the command you give it as a single String parameter. You can help the method with parsing by providing an array of Strings.
I suggest you read the article and also the javadoc for class java.lang.ProcessBuilder.
The code I ended up using executes some AppleScript code: (as DanielPryden suggested)
public static void main(String[] args){
if(args.length == 0 && System.getProperty("os.name").toLowerCase().contains("mac")){
try {
String path = new File(Main.class.getProtectionDomain().getCodeSource().getLocation().toURI()).getAbsolutePath();
String command = "tell application \"Terminal\"\n" +
"do script \"java -jar \'" + path + "\' isInConsole\"\n" +
"close the front window\n" + // because "do script..." opens another window
"activate\n" +
"end tell";
String[] arguments = new String[]{"osascript", "-e", command};
Runtime.getRuntime().exec(arguments);
System.exit(0);
} catch (IOException | URISyntaxException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
// program continues...
}
Related
I am creating a Java Swing application where I am taking input from user which will be used as arguments for a batch file.
After pressing a button, my batch file should get launched in ADMIN mode along with the arguments provided by user.
This is the command line:
powershell Start-Process -Verb runas cmd '/k System.getProperty("user.dir") + batchFilePath +arg1+ "" +arg2 + "" +arg3'
This command is working properly when we paste it in CMD window.
But I want code for a Java application so that we can run it from JButton.
So I used ArrayList and given this ArrayList as input to ProcessBuilder but I am getting an error.
code:
String launchCMD= System.getProperty("user.dir") + batchFilePath +arg1+ "" +arg2 + "" +arg3
final ArrayList<String> commands = new ArrayList<String>();
commands.add("powershell Start-Process -Verb runas cmd \'/k ")
commands.add(launchCMD)
commands.add("\'" );
ProcessBuilder pr=new ProcessBuilder(commands);
pr.start();
error:
java.io.IOException: Cannot run program ... : CreateProcess error=2, The system cannot find the file specified
Your question is not really related to Swing, it is related to how to use class ProcessBuilder and that class is not a terminal emulator. It does not parse a command line that you enter into a PowerShell window. You need to split the command into tokens. That's why the ProcessBuilder constructor takes a list of strings. Note that the parameters to ProcessBuilder constructor do not have to be string literals. You can create a string any way you like and pass it as an argument to the ProcessBuilder constructor.
You didn't post a sample batch file so I wrote one of my own, named showargs.bat which simply echoes its first argument.
#echo off
echo %1
The command that worked for me (i.e. ran the batch file as administrator) was:
powershell.exe -Command Start-Process cmd.exe -ArgumentList '/k C:\Users\USER\showargs.bat Diego' -Verb RunAs
Java code that executes the above command using class ProcessBuilder:
ProcessBuilder pb = new ProcessBuilder("powershell.exe",
"-Command",
"Start-Process",
"cmd.exe",
"-ArgumentList",
"'/k C:\\Users\\USER\\showargs.bat Diego'",
"-Verb",
"RunAs");
try {
Process proc = pb.start();
int status = proc.waitFor();
System.out.println("status = " + status);
}
catch (InterruptedException | IOException x) {
x.printStackTrace();
}
I've done many research for executing an external program (e.g. iTunes) by some simple code, however the suggestions did never work. Sometimes nothing happend, sometimes I got this error message:
English: Unable to find "Discord". Be sure the name is written correctly and try again.
My Code is the following:
try {
String name = (String) "start " + table.getValueAt(table.getSelectedRow(), table.getSelectedColumn());
ProcessBuilder p = new ProcessBuilder("cmd.exe", "cd /D %HOMEDRIVE%%HOMEPATH%/Desktop", "/c", name);
p.start();
} catch (Exception e) {
e.printStackTrace();
}
In my example I get the name of the external program from a JTable, this part is working fine. The ProcessBuilder is changing the directory to the desktop first. Then the external program should be executed by the start <program name> command. With this code I get the mentioned error message.
If you have a solution with cmd, please include changing the directory to the desktop.
You should pass each argument as a single entry to ProcessBuilder. In your current code, you sometimes take multiple arguments together (like cd /D %HOME...). Try passing every argument as it's own parameter, including the command to start and its argument:
String name = (String) table.getValueAt(table.getSelectedRow(), table.getSelectedColumn());
ProcessBuilder p = new ProcessBuilder("cmd.exe", "cd", "/D", "%HOMEDRIVE%%HOMEPATH%/Desktop", "/c", "start", name);
According to: cmd.exe,
/D Ignore registry AutoRun commands
HKLM | HKCU \Software\Microsoft\Command Processor\AutoRun
Did you mean start.exe /D not cmd.exe /D?
and also they told that
If /C or /K is specified, then the remainder of the command line is processed as an immediate command in the new shell. Multiple commands separated by the command separator '&' or '&&' are accepted if surrounded by quotes.
Did you mean cmd.exe /C "cd %HOMEDRIVE%%HOMEPATH%\Desktop & Discord"?
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.
In my Java application, I want to run a batch file that calls "scons -Q implicit-deps-changed build\file_load_type export\file_load_type"
It seems that I can't even get my batch file to execute. I'm out of ideas.
This is what I have in Java:
Runtime.
getRuntime().
exec("build.bat", null, new File("."));
Previously, I had a Python Sconscript file that I wanted to run but since that didn't work I decided I would call the script via a batch file but that method has not been successful as of yet.
Batch files are not an executable. They need an application to run them (i.e. cmd).
On UNIX, the script file has shebang (#!) at the start of a file to specify the program that executes it. Double-clicking in Windows is performed by Windows Explorer. CreateProcess does not know anything about that.
Runtime.
getRuntime().
exec("cmd /c start \"\" build.bat");
Note: With the start \"\" command, a separate command window will be opened with a blank title and any output from the batch file will be displayed there. It should also work with just `cmd /c build.bat", in which case the output can be read from the sub-process in Java if desired.
Sometimes the thread execution process time is higher than JVM thread waiting process time, it use to happen when the process you're invoking takes some time to be processed, use the waitFor() command as follows:
try{
Process p = Runtime.getRuntime().exec("file location here, don't forget using / instead of \\ to make it interoperable");
p.waitFor();
}catch( IOException ex ){
//Validate the case the file can't be accesed (not enought permissions)
}catch( InterruptedException ex ){
//Validate the case the process is being stopped by some external situation
}
This way the JVM will stop until the process you're invoking is done before it continue with the thread execution stack.
Runtime runtime = Runtime.getRuntime();
try {
Process p1 = runtime.exec("cmd /c start D:\\temp\\a.bat");
InputStream is = p1.getInputStream();
int i = 0;
while( (i = is.read() ) != -1) {
System.out.print((char)i);
}
} catch(IOException ioException) {
System.out.println(ioException.getMessage() );
}
ProcessBuilder is the Java 5/6 way to run external processes.
To run batch files using java if that's you're talking about...
String path="cmd /c start d:\\sample\\sample.bat";
Runtime rn=Runtime.getRuntime();
Process pr=rn.exec(path);`
This should do it.
The executable used to run batch scripts is cmd.exe which uses the /c flag to specify the name of the batch file to run:
Runtime.getRuntime().exec(new String[]{"cmd.exe", "/c", "build.bat"});
Theoretically you should also be able to run Scons in this manner, though I haven't tested this:
Runtime.getRuntime().exec(new String[]{"scons", "-Q", "implicit-deps-changed", "build\file_load_type", "export\file_load_type"});
EDIT: Amara, you say that this isn't working. The error you listed is the error you'd get when running Java from a Cygwin terminal on a Windows box; is this what you're doing? The problem with that is that Windows and Cygwin have different paths, so the Windows version of Java won't find the scons executable on your Cygwin path. I can explain further if this turns out to be your problem.
Process p = Runtime.getRuntime().exec(
new String[]{"cmd", "/C", "orgreg.bat"},
null,
new File("D://TEST//home//libs//"));
tested with jdk1.5 and jdk1.6
This was working fine for me, hope it helps others too.
to get this i have struggled more days. :(
I had the same issue. However sometimes CMD failed to run my files.
That's why i create a temp.bat on my desktop, next this temp.bat is going to run my file, and next the temp file is going to be deleted.
I know this is a bigger code, however worked for me in 100% when even Runtime.getRuntime().exec() failed.
// creating a string for the Userprofile (either C:\Admin or whatever)
String userprofile = System.getenv("USERPROFILE");
BufferedWriter writer = null;
try {
//create a temporary file
File logFile = new File(userprofile+"\\Desktop\\temp.bat");
writer = new BufferedWriter(new FileWriter(logFile));
// Here comes the lines for the batch file!
// First line is #echo off
// Next line is the directory of our file
// Then we open our file in that directory and exit the cmd
// To seperate each line, please use \r\n
writer.write("cd %ProgramFiles(x86)%\\SOME_FOLDER \r\nstart xyz.bat \r\nexit");
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
// Close the writer regardless of what happens...
writer.close();
} catch (Exception e) {
}
}
// running our temp.bat file
Runtime rt = Runtime.getRuntime();
try {
Process pr = rt.exec("cmd /c start \"\" \""+userprofile+"\\Desktop\\temp.bat" );
pr.getOutputStream().close();
} catch (IOException ex) {
Logger.getLogger(MainFrame.class.getName()).log(Level.SEVERE, null, ex);
}
// deleting our temp file
File databl = new File(userprofile+"\\Desktop\\temp.bat");
databl.delete();
The following is working fine:
String path="cmd /c start d:\\sample\\sample.bat";
Runtime rn=Runtime.getRuntime();
Process pr=rn.exec(path);
This code will execute two commands.bat that exist in the path C:/folders/folder.
Runtime.getRuntime().exec("cd C:/folders/folder & call commands.bat");
import java.io.IOException;
public class TestBatch {
public static void main(String[] args) {
{
try {
String[] command = {"cmd.exe", "/C", "Start", "C:\\temp\\runtest.bat"};
Process p = Runtime.getRuntime().exec(command);
} catch (IOException ex) {
}
}
}
}
To expand on #Isha's anwser you could just do the following to get the returned output (post-facto not in rea-ltime) of the script that was run:
try {
Process process = Runtime.getRuntime().exec("cmd /c start D:\\temp\\a.bat");
System.out.println(process.getText());
} catch(IOException e) {
e.printStackTrace();
}
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");
}
}