I'm writing a library to cover WMctrl shell program. I have problem with resizing windows:
String command = "wmctrl -r \"Calculator\" -e 0,100,100,500,500";
System.out.println(command);
String output = this.bashCommandExecutor.execute(command);
System.out.println(output);
This doesn't work - output variable is empty. But when I copy-paste wmctrl -r "Calculator" -e 0,100,100,500,500 to the Terminal it works properly.
Other commands like "wmctrl -d" and "wmctrl -l" work in this.bashCommandExecutor.execute() method.
This method looks like this:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class BashCommandExecutor
{
String execute(String bashCommand)
{
Process p = null;
try {
p = Runtime.getRuntime().exec(bashCommand);
p.waitFor();
}
catch (InterruptedException | IOException e) {
e.printStackTrace();
}
BufferedReader reader = new BufferedReader(
new InputStreamReader(p.getInputStream())
);
String line = "";
StringBuilder sb = new StringBuilder();
try {
while ((line = reader.readLine())!= null) {
sb.append(line + "\n");
}
} catch (IOException e) {
e.printStackTrace();
}
return sb.toString();
}
}
Why does resizing work in command line, but doesn't work in Java app?
I have found that using the ProcessBuilder class rather than Runtime.exec() works to run the wmctrl command you described.
So rather than:
String bashCommand = "wmctrl -r \"Calculator\" -e 0,100,100,500,500";
Process p = Runtime.getRuntime().exec(bashCommand);
You can try:
String bashCommand = "wmctrl -r \"Calculator\" -e 0,100,100,500,500";
ProcessBuilder pb = new ProcessBuilder("bash", "-c", bashCommand);
Process p = pb.start();
Here is a link to another post explaining the different between Runtime.exec() and using ProcessBuilder.
Related
I'm trying to send docker commands using Java Runtime.
Commands like docker cp works very nice with the below method as well as typing directly from the terminal.
First problem is that the docker exec command works only from the terminal, not with the Java Runtime. Other docker commands like docker cp works as expected. The only problem is that I can't run commands on the container, like echoing on the container's terminal.
Also the 2nd problem is that the System.out.println(...)method in the below method, doesn't actually print anything.
private static void runCommand() throws IOException, InterruptedException {
Process proc = Runtime.getRuntime().exec(
new String[]{"/bin/sh",
"-c",
"docker exec -u 0 -it <CONTAINER_NAME> echo", "'abc'"});
BufferedReader reader =
new BufferedReader(new InputStreamReader(proc.getInputStream()));
String line = "";
while ((line = reader.readLine()) != null) {
System.out.print(line + "\n");
}
proc.waitFor();
}
There is no need to run docker inside a shell. You can start the process directly.
As of Java 1.7 you can also use ProcessBuilder.inheritIO() to redirect the standard I/O of the subprocess
Below a working example that prints the output of the echo command:
ProcessBuilder processBuilder = new ProcessBuilder();
processBuilder.command("docker", "exec" , "-it", "<CONTAINER_NAME_OR_ID>", "echo", "abc").inheritIO();
try {
Process process = processBuilder.start();
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
int exitCode = process.waitFor();
System.out.println("\nExited with error code : " + exitCode);
} catch (Exception e) {
e.printStackTrace();
}
Hope this helps.
I'm currently working on a Plugin which also can execute commands in the Linux shell. I've setup some commands that I will need later. Killing screens is already working but now I want to start some screen's through my Plugin. But this isn't working.
I've tried to change from Runtime.getRuntime.exec(cmd); to a ProcessBuilder worked for killing screens.
My method to execute commands
public static void shellExecuteBuilder(String[] command, Player p, boolean bool, String Wrapper) {
ProcessBuilder prb;
try {
prb = new ProcessBuilder(command);
prb.redirectErrorStream(true);
Process pro = prb.start();
BufferedReader r = new BufferedReader(new InputStreamReader(pro.getInputStream()));
String line;
while (prb.redirectErrorStream() == true) {
line = r.readLine();
if (line != null) {
System.out.println(line);
}
}
if (bool == true) {
p.sendMessage("§aDer Wrapper §e" + Wrapper + " §awurde angehalten!");
} else {
p.sendMessage("§aDer Wrapper §e" + Wrapper + " §awurde gestartet!");
}
} catch (IOException e) {
if (bool == true) {
p.sendMessage("§cDer Wrapper §e" + Wrapper + " §ckonnte nicht angehalten werden!");
} else {
p.sendMessage("§cDer Wrapper §e" + Wrapper + " §ckonnte nicht gestartet werden!");
}
e.printStackTrace();
}
}
How I setup this in another method
String[] command = new String[] {"/bin/sh", "-c", "cd /path/to/my/script/", "./start.sh"};
shellExecuteBuilder(command, p, false, Wrapper);
I expected that my method will startup a new screen but it actually does not do anything.
/bin/sh can accept a sequence of commands, but formatted differently than you have it. Try this instead:
String[] command = new String[] {"/bin/sh", "-c", "cd /path/to/my/script/; ./start.sh"};
Also, the way you process the output from the process will never end. The while loop should be testing the returned output from the process, not the redirection setting (which will never change). Here's a modified version that properly reads the output and wait for the subshell to exit. Note that I removed some of your app's variables (bool, Wrapper, Player) to simplify the example:
import java.io.InputStreamReader;
import java.io.IOException;
import java.io.BufferedReader;
public class TestPB {
public static void shellExecuteBuilder(String[] command) {
ProcessBuilder prb;
try {
prb = new ProcessBuilder(command);
prb.redirectErrorStream(true);
Process pro = prb.start();
BufferedReader r = new BufferedReader(new InputStreamReader(pro.getInputStream()));
String line;
while( (line = r.readLine()) != null) {
System.out.println(line);
}
pro.waitFor();
} catch (Exception e) {
e.printStackTrace();
}
}
public static final void main(String[] args) {
TestPB tpb = new TestPB();
String[] command = new String[] {"/bin/sh", "-c", "cd /path/to/my/script/; ./start.sh"};
TestPB.shellExecuteBuilder(command);
}
}
Alternatively, instead of telling the shell to change directory ProcessBuilder can do it, although not with the way you currently have your program 'factored':
ProcessBuilder prb = new ProcessBuilder ("/bin/sh", "-c", "./startsh");
prb.directory("/path/to/my/script");
prb.redirectErrorStream(true);
Process pro = prb.start();
or using 'fluent' style all in one statement:
Process pro = new ProcessBuilder ("/bin/sh", "-c", "./startsh")
.directory("/path/to/my/script").redirectErrorStream(true).start();
I am trying to execute the following line in java(with escaped characters):
"psexec -i -d \\\\computerName -u user -p pass calc 2>
somePath\\psexecOut.txt"
I use the following method to execute cmd lines:
private static String executeCommand(String command) {
StringBuffer output = new StringBuffer();
System.out.println("command is = \n"+command);
Process p;
try {
p = Runtime.getRuntime().exec(command);
p.waitFor();
BufferedReader reader =
new BufferedReader(new InputStreamReader(p.getInputStream()));
String line = "";
while ((line = reader.readLine())!= null) {
output.append(line + "\n");
}
} catch (Exception e) {
e.printStackTrace();
}
return output.toString();
}
The line is executed and calc is starting , but the log part doesn't work. The log file psexecOut.txt is not created.
When I run the command normally (without excaped characters) in cmd it runs fine and the log file is created, but using java it doesn't create the log file.
I suspect that > needs to be escaped but as I read it's already escaped as it is.
How can I execute the psexec with log to text file in a single cmd line using java like I can do manually in windows console ?
Solved:
As lit suggested in the comments: cmd.exe /c works.
So the corrected method is:
private static String executeCommand(String command) {
StringBuffer output = new StringBuffer();
System.out.println("command is = \n"+command);
Process p;
try {
p = Runtime.getRuntime().exec("cmd.exe /c "+command); // <-correction done here
p.waitFor();
BufferedReader reader =
new BufferedReader(new InputStreamReader(p.getInputStream()));
String line = "";
while ((line = reader.readLine())!= null) {
output.append(line + "\n");
}
} catch (Exception e) {
e.printStackTrace();
}
return output.toString();
}
Try putting cmd.exe /C at the beginning of the command? It is cmd.exe that interprets the > redirection.
"cmd.exe /C psexec -i -d \\\\computerName -u user -p pass calc 2> somePath\\psexecOut.txt"
I executed the ldapsearch command through java. See the below code.
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class TestMain
{
public static void main(String[] args) throws InterruptedException, IOException
{
String ldspCmd ="ldapsearch -ZZ -h ldap-url.com -x -D cn=username,ou=webapps,ou=ec,o=uoa -w $(echo PassWord | base64 -di) -b ou=ec_users,dc=ec,dc=auckland,dc=ac,dc=nz \"(groupMembership=cn=bpmusers,ou=ec_group,dc=ec,dc=auckland,dc=ac,dc=nz)\"";
String output = executeCommand(ldspCmd);
System.out.println(output);
}
private static String executeCommand(String command) {
StringBuffer output = new StringBuffer();
Process p;
try {
p = Runtime.getRuntime().exec(command);
p.waitFor();
BufferedReader reader =
new BufferedReader(new InputStreamReader(p.getInputStream()));
String line = "";
while ((line = reader.readLine())!= null) {
output.append(line + "\n");
}
BufferedReader errorReader =
new BufferedReader(new InputStreamReader(p.getErrorStream()));
String erroLine = "";
while ((erroLine = errorReader.readLine())!= null) {
output.append(erroLine + "\n");
}
} catch (Exception e) {
e.printStackTrace();
}
return output.toString();
}
}
But I get the error "ldapsearch: unable to parse debug value "i)"".
But when I execute the same command through command line it executes correctly and returns the records.
What am I doing wrong here? Can anyone help me to sort this?
Constructs like $(echo PassWord | base64 -di) inside your argument list are interpreted and handled by your shell. And when you invoke a command from Java using Runtime.exec, you are not using a shell, you are passing the command directly to the operating system, so you don't get the benefit of the shell interpreting these constructs.
If you want those benefits, you need to explicitly invoke the shell.
Also, Java doesn't have the same complex logic to split arguments to a command that a shell does. Java just cuts the argument list at space characters.
So in your executeCommand method you have a line:
p = Runtime.getRuntime().exec(command);
You should change that to:
// Add shell invocation around the above command
String[] shellCommand = { "/bin/bash", "-c", command };
p = Runtime.getRuntime().exec(shellCommand);
I am running my Java program from terminal and I am trying to count the number of files in a certain directory using a linux command in my code; I have managed to get output for all other commands but this one.
My command is: ls somePath/*.xml | wc -l
When I run my command in my code, it appears that it has nothing to output, yet when I run the same exact command in terminal it works just fine and actually outputs the number of xml files in that directory.
Here is my code:
private String executeTerminalCommand(String command) {
String s, lastOutput = "";
Process p;
try {
p = Runtime.getRuntime().exec(command);
BufferedReader br = new BufferedReader(
new InputStreamReader(p.getInputStream()));
System.out.println("Executing command: " + command);
while ((s = br.readLine()) != null){//it appears that it never enters this loop since I never see anything outputted
System.out.println(s);
lastOutput = s;
}
p.waitFor();
p.destroy();
} catch (Exception e) {
e.printStackTrace();
}
return lastOutput;//returns empty string ""
}
Updated code w/ output
private String executeTerminalCommand(String command) {
String s, lastOutput = "";
try {
Process p = new ProcessBuilder().command("/bin/bash", "-c", command).inheritIO().start();
//Process p = Runtime.getRuntime().exec(command);
BufferedReader br = new BufferedReader(
new InputStreamReader(p.getInputStream()));
System.out.println("Executing command: " + command);
while ((s = br.readLine()) != null){
System.out.println("OUTPUT: " + s);
lastOutput = s;
}
System.out.println("Done with command------------------------");
p.waitFor();
p.destroy();
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("LAST OUTPUT IS: " + lastOutput);
return lastOutput;
}
output:
Executing command: find my/path -empty -type f | wc -l
Done with command------------------------
1
LAST OUTPUT IS:
To execute a pipeline, you have to invoke a shell, and then run your commands inside that shell.
Process p = new ProcessBuilder().command("bash", "-c", command).start();
bash invokes a shell to execute your command and -c means commands are read from string. So, you don't have to send the command as an array in ProcessBuilder.
But if you want to use Runtime then
String[] cmd = {"bash" , "-c" , command};
Process p = Runtime.getRuntime().exec(cmd);
Note: You can check advantages of ProcessBuilder here and features here over Runtime