ssh to remote host using sshpass and fetch results using java - java

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.

Related

Sending 'exec' commands to Docker container using Java

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.

Why does the output of my shell script doesn't print in console?

I am trying to print the output of a shell script on the console using java. When I manually run the script, I get
C:/Users/user1/Desktop/shell.sh: line 78: /usr/ucb/ps: No such file or directory
<STATUS>: Probe [ devicename ] is not running!
But, when I try to run it on my Java program, the output is not being printed on the console.
My code is:
ProcessBuilder processBuilder = new ProcessBuilder("C:/Program Files/Git/git-bash.exe","C:/Users/user1/Desktop/shell.sh");
try {
Process process = processBuilder.start();
StringBuilder output = new StringBuilder();
BufferedReader reader = new BufferedReader(
new InputStreamReader(process.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
output.append(line + "\n");
System.out.println(line);
}
int exitVal = process.waitFor();
if (exitVal == 0) {
System.out.println("Success!");
System.out.println(output);
System.exit(0);
} else {
//abnormal...
}
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
The only output I am getting is "Success". When I debugged my code, I found that the code never enters the condition
while ((line = reader.readLine()) != null)
even though, in the bash terminal, there are lines of output. Why is this happening?
I am stuck at this point and I couldn't find any other explanations for this problem. Kindly help.
You are running git-bash.exe which opens as a windows application. Although Java has access to the stdout/stderr streams of git-bash.exe, these are not necessarily the same as the stdout/err of the internal launch of your shell script within git-bash.exe.
One way to see the stdout/err of your command would be to make a java friendly version of the .sh script which launches your original sh and redirects output to a specific files which you can then access within java afterwards.
ProcessBuilder pb = new ProcessBuilder("C:/Program Files/Git/GIT-BASH.EXE","/c/Users/blah/somescript.sh");
somescript.sh:
#!/bin/sh
runtheoriginalcommand > ~/somepath.out 2> ~/somepath.err
You could also add extra args to wrapper to pass the out/err files to be used so there is no contention with any other launches or hardcoded output files.

Check in Java if a certain application is in focus

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)

Difference when executing linux command from terminal and java runtime process

I'm looking a way to write running log of python which is executed by java app via script.
Let's say my script is:
import time
for x in range(120):
print("Running ", x)
time.sleep(1)
Here is my current solution:
Trigger script using java
String cmd = "python script.py";
var process = Runtime.getRuntime().exec(cmd, null, new File(sandboxPath));
Write log to new file:
String traceLogCmd = String.format("strace -p %s -s 9999 -e trace=write -o output.txt", process.pid());
Runtime.getRuntime().exec(traceLogCmd, null, new File(sandboxPath));
Now the problem is output.txt only has content whenever the python script is done executing so that I cannot tailf the output file.
Meanwhile if I execute python script.py and strace command dirrectly from terminal, the output is exactly what I expected.
Can someone correct me if I did something wrong or have a another way to get python log?
Thanks in advance.
Use ProcessBuilder instead of Runtime.exec(). More details: When Runtime.exec() won't
The following code will append to StringBuilder object output of the script sb.append(line);. It would not be difficult to write that content to a file.
Process p = new ProcessBuilder("sh", "-c", "python", "path-to-your-script").start();
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();
}

java run bash script, command not found

I tried to use java to run some bash script and store the terminal output in a string. However, there are a lot of commands don't work in this way. It keeps showing command not found, but I can run those commands correctly in terminal, ex node --version, go --version. I guess is the path issue, but have no idea how to fix it.
Another question, when I run "python --version", it shows "Python 2.7.10" but it is in getErrorStream. Can anyone give me some hint?
public static void runscript() throws IOException {
Runtime rt = Runtime.getRuntime();
String[] commands = { "/bin/bash", "-c", "node --version" };
Process proc = null;
try {
proc = rt.exec(commands);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
BufferedReader stdInput = new BufferedReader(new InputStreamReader(proc.getInputStream()));
BufferedReader stdError = new BufferedReader(new InputStreamReader(proc.getErrorStream()));
// read the output from the command
System.out.println("Here is the standard output of the command:\n");
String s = null;
while ((s = stdInput.readLine()) != null) {
System.out.println(s);
}
// read any errors from the attempted command
System.out.println("Here is the standard error of the command (if any):\n");
while ((s = stdError.readLine()) != null) {
System.out.println(s);
}
}
Reply #VishalKamat comment.
when I tried using the output of "which node" as my path, which is "/usr/local/bin/node". It works!!!
But, does that mean I have to change the path when I need to get different application version info?
I thought I can easily get the info just like I do in terminal.
I try to print $PATH by java in this way
String[] commands = { "/bin/bash","-c", "$PATH" };
The error msg is :
/bin/bash: /usr/bin:/bin:/usr/sbin:/sbin: No such file or directory

Categories

Resources