I'd like to know whether a certain application is in focus in Linux. Say it is Google Chrome. To do so, I wrote a bash on-liner which does it correctly.
xdotool search --name --class 'google-chrome' | grep $(xdotool getactivewindow)
When this command is run in terminal, it will print the id of the terminal itself. To avoid that, run the following command and select Chrome in the three seconds time span.
sleep 3; xdotool search --name --class 'google-chrome' | grep $(xdotool getactivewindow)
The problem is that when I run the above-mentioned one-liner from Java, it seems to always print nothing. Here's my code:
String cmd = "xdotool search --name --class 'google-chrome' | grep $(xdotool getactivewindow)";
Process p = Runtime.getRuntime().exec(cmd);
String result = getCommandResult(p.getInputStream());
private static String getCommandResult(InputStream stream) throws IOException {
StringBuilder sb = new StringBuilder();
try (InputStreamReader isr = new InputStreamReader(stream);
BufferedReader in = new BufferedReader(isr)) {
String line;
while ((line = in.readLine()) != null) {
sb.append(line);
}
}
return sb.toString().trim();
}
I'm open to different solutions to resolving this problem.
As barti_ddu said, this is not working because of the pipe in the command. You can workaround this by creating one sh process with your command passed as the argument:
String cmd = "xdotool search --name --class 'google-chrome' | grep $(xdotool getactivewindow)";
Process p = new ProcessBuilder("sh", "-c", cmd).start();
String result = getCommandResult(p.getInputStream());
If You are using pipe redirection, then You either have to invoke the shell, or redirect the output from one program to another yourself.
However, IMHO, You do not need grep at all, just:
Enumerate windows of certain class.
Get the active window id and check if the active window list contains it.
Example (q&d, resource/error handling omitted):
private static List<String> exec(String... args) throws IOException {
List<String> result = new ArrayList<>();
String line;
BufferedReader reader = new BufferedReader(
new InputStreamReader(
new ProcessBuilder()
.command(args)
.start()
.getInputStream()));
while ((line = reader.readLine()) != null) {
result.add(line);
}
return result;
}
public static void main(String[] args) {
try {
Thread.sleep(3000);
String windowId = exec("xdotool", "getactivewindow").get(0);
System.out.println(windowId);
List<String> windowList = exec("xdotool", "search", "--name", "--class", "google-chrome");
System.out.println(windowList.contains(windowId));
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
Why are you hard coding the command into your class? Rather put your command in a shell script and call that instead. Then you have the flexibility to change the command without having to re-compile.
Process proc = Runtime.getRuntime().exec("./opt/scripts/myscript.sh");
... depending on your application, it would potentially be even better to pass in the shell script as parameter.
private void runCommandLine(String script) {
try {
Process proc = Runtime.getRuntime().exec(script);
proc.waitFor();
int character;
while((character = proc.getInputStream().read()) != -1) {
System.out.write(character);
}
while((character = proc.getErrorStream().read()) != -1) {
System.err.write(character);
}
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
EDIT:
If you goal is to figure out what window is currently in focus, then instead of trying to execute something via the command line, you could just use the JNA (Java Native Access) libraries to potentially do this.
Find out what application (window) is in focus in Java
Java Native Access (Github)
Related
Issue while executing hadoop command from java.lang.Process.
hadoop fs -rm -R -skipTrash pathToFolder
this command directly executed on unixbox is working but when I try to execute it from Process it says '-rm -R' unknown command.
public class Test1 {
public static void main(String[] args) throws IOException {
String[] commandToDelete = new String[]{"hadoop", "fs","-rm -R", "-skipTrash", "hdfs://pathToFolder"};
Process process = Runtime.getRuntime().exec(commandToDelete);
try {
process.waitFor();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(process.exitValue());
BufferedReader errorReader = new BufferedReader(
new InputStreamReader(process.getErrorStream()));
String line = null;
while ((line = errorReader.readLine()) != null) {
System.out.println(line);
}
errorReader.close();
}
}
From same location I am able to delete file but not folder any suggestions please.
From exec(String[] cmdarray)
Executes the specified command and arguments in a separate process.
This is a convenience method. An invocation of the form exec(cmdarray) behaves in exactly the same way as the invocation exec(cmdarray, null, null).
So you below command is run in separate process thats why -rm -R is unknown command:
String[] commandToDelete = new String[]{"hadoop", "fs","-rm -R", "-skipTrash", "hdfs://pathToFolder"};
Run like below:
String command = "hadoop fs -rm -R -skipTrash hdfs://pathToFolder"
Process process = Runtime.getRuntime().exec(command);
A more robust solution (that prevents your subsequent problem with space in the path) is to stay with the String[] form but split the arguments correctly:
String[] commandToDelete = new String[]{"hadoop", "fs", "-rm", "-R", "-skipTrash", "hdfs://pathToFolder"};
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'm trying to run some commands on a remote machine and capture the result using Java. I have a shell script called test.sh which has following commands:
sshpass -p 'password' ssh root#host.com echo hostname
I'm running it using below java code:
public void runCommand() throws IOException, InterruptedException {
ProcessBuilder builder = new ProcessBuilder();
boolean isWindows = System.getProperty("os.name").toLowerCase().startsWith("windows");
if (isWindows) {
builder.command("cmd.exe", "/c", "dir");
} else {
builder.command("sh", "-c", "sh test.sh");
}
builder.directory(new File(System.getProperty("user.home")));
Process process;
BufferedReader reader = null;
try {
process = builder.start();
reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
StringBuilder stringBuilder = new StringBuilder();
String line = null;
while ((line = reader.readLine()) != null) {
stringBuilder.append(line);
}
String output = stringBuilder.toString();
System.out.println(output);
} finally
{
if (reader != null)
try {
reader.close();
} catch (IOException e) {
}
}
}
The command executes but I'm not getting anything in the output. If I use simple commands like echo, hostname then I'm able to get the result in output. I know of JSch which can solve the problem, but I can't use it.
When starting a Process in Java, you must consume both stdout and stderr to avoid blocking, and you should log or control both (avoid consume-to-discard). There are now easier solutions than what the linked article mentions, using ProcessBuilder.
In this instance you completely ignore error output from your command. You said your process exits with status code 127, so it probably prints on stderr so you will obtain more details about the error by using ProcessBuilder.redirectErrorStream(true).
Probably sshpass not installed, or installed but not in $PATH for your java process.
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 want to get total RAM on Android:
private String getTotalRAM()
{
ProcessBuilder cmd;
String result="";
try{
String[] args = {"/system/bin/sh", "-c", "cat -n /proc/meminfo | grep MemTotal"};
cmd = new ProcessBuilder(args);
Process process = cmd.start();
InputStream in = process.getInputStream();
byte[] re = new byte[1024];
while(in.read(re) != -1){
System.out.println(new String(re));
result = result + new String(re);
}
in.close();
} catch(IOException ex){
ex.printStackTrace();
}
return result;
}
If there are not grep MemTotal, cat returns me a whole info about memory. When I want to get just one line with grep, I get nothing. How can i fix this? I just want to get total available RAM at this moment.
All kinds of redirections (|, >, <, ...) are handled by the shell. If you don't invoke the shell, then you can't use those.
A clean solution would be to read /proc/meminfo in your Java code and simple search for the String MemTotal manually. The code wouldn't be much longer than what you're doing now and would need a lot less resurces.
As #Joachim suggests you are likely to find this works for you.
BufferedReader pmi = new BufferedReader(new FileReader("/proc/meminfo"));
try {
String line;
while ((line = pmi.readLine()) != null)
if (line.contains("MemTotal"))
// get the second word as a long.
return Long.parseLong(line.split(" +",3)[1]);
return -1;
} finally {
pmi.close();
}