Java Runtime.exec() with ssh on Linux Issues - java

I am trying to start a new process using Runtime.exec(), but my problem lies within using ssh to remote in and then run a java program there. Code:
test = "ssh -t username#host java packageName.ClassName portNumber (Other command line args for this class)"
Process proc = Runtime.getRuntime().exec(new String[] {"/bin/bash", "-c", test});
this doesn't fail or catch, but I need to be able to see the stdout for the newly running process and I don't.
Note: if I run ssh -t username#host java packageName.ClassName portNumber (Other command line args for this class) from the command line it works fine. I have the host setup to not require a password by using ssh keys.
Any ideas?

You need to use Process.getInputStream to obtain the output from the sub-process being created.
See this article for a good discussion on Runtime.exec.

I think you can ask for an input stream that corresponds to the stdout of the process and then print it on your standard output. If you need to see it after it executes, just call waitFor() method on the process so it finishes before you start printing.

Use getInputStream() to access returned process's stdout. You can also use facilities provided by ProcessBuilder.Redirect.

Related

Why is my runtime command ignored after calling a batch file?

public class Test_Python
{
public static void main( String[] args ) throws IOException
{
String command = "cmd /k start cmd.exe /k \"cd C:\\Workspace\\supply\\environment\\ && setup.bat && python -V ";
Runtime.getRuntime().exec(command);
}
}
When I execute my code, the "python -V" does not work, it is not executed in the cmd window.
But if I delete the "setup.bat" from my command, the "python -V" is executed (I can read the version in the cmd window)
Also, if I manually start a cmd, type setup.bat and then python -V it works.
My batch file is used to set my working environment:
SET basedir=%~dp0
echo %basedir%
cmd /k "cd %basedir%\..\scripts && set PYTHONPATH=%basedir%\..\lib"
Is there any way to know why it fails?
Unlike python Java may need some help. As I can see you are running on Windows.
You invoke the Runtime.exec() method. The method returns a Process instance, and in it's documentation you can read
By default, the created process does not have its own terminal or console. All its standard I/O (i.e. stdin, stdout, stderr) operations will be redirected to the parent process, where they can be accessed via the streams obtained using the methods getOutputStream(), getInputStream(), and getErrorStream(). The parent process uses these streams to feed input to and get output from the process. Because some native platforms only provide limited buffer size for standard input and output streams, failure to promptly write the input stream or read the output stream of the process may cause the process to block, or even deadlock.
So it is likely your process is started by the OS but gets blocked due to I/O restrictions. Get around that by reading the STDOUT and STDERR streams until your process finishes. One good programming model is visible at https://www.baeldung.com/run-shell-command-in-java
Now that we know the OS is not blocking the process, the issue may be inside the command itself. Note you concatenated several commands using the && operator. I do not have a windows system but searched a bit online:
https://www.ionos.com/digitalguide/server/know-how/windows-cmd-commands/
CommandA && CommandB (the second command is only run if the first was successful)
To check how the first part of your command exits run it separately in Java and do not forget to print the process.exitValue() method.
But looking at the whole picture, all you do with the first part is to change the working directory and set some environment variable. What stops you from running the ProcessBuilder methods? The example in the documentation directly sets the environment and directory.

Pass arguments and run .cmd file in windows using java

I want to run and pass the arguments to ".cmd" file using Java program. I have checked the existing Solutions, but nothing is working in my case.
From Command line, I am running below command after getting into Directory C:/users/project/solr/bin
solr.cmd -s "C:users/github/example/solr-config"
So, solr.cmd gets arguments from the other Directory and then it runs the solr instance.
I have tried this, but I am not sure how to provide Parameters to Runtime.getRuntime():
Runtime run = Runtime.getRuntime();
Process p = null;
String cmd = "cmd /c start C:/users/project/solr/bin C:users/github/example/solr-config";
Process pr = run.exec(cmd);
I have followed this link: How do I run a batch file from my Java Application?
Could anyone please help me this.
I solved it by modifying cmd as solr.cmd expects -s also as argument:
String cmd = "cmd /c start C:/users/project/solr/bin/solr.cmd -s C:users/github/example/solr-config;
There is a difference between "it doesn't work" and "it doesn't show anything". As per the documentation:
By default, the created subprocess does not have its own terminal or console. All its standard I/O (i.e. stdin, stdout, stderr) operations will be redirected to the parent process, where they can be accessed via the streams obtained using the methods getOutputStream(), getInputStream(), and getErrorStream(). The parent process uses these streams to feed input to and get output from the subprocess. Because some native platforms only provide limited buffer size for standard input and output streams, failure to promptly write the input stream or read the output stream of the subprocess may cause the subprocess to block, or even deadlock.
If you want to retrieve the data that your process might have printed out on the standard I/O, you will have to read them from the abovementioned streams.

Getting error while executing the docker command from java program

I tried to execute the following docker command from Java code:
command: ***docker exec -it reverent_hoover date***
(Here, reverente_hoover is the container name.)
When I executed the above command from Linux, it gave me the following output:
Wed May 6 05:19:28 UTC 2015
But when I tried to execute it from Java code, it gave me this error:
time="2015-05-05T19:31:19+05:30" level="fatal" msg="cannot enable tty
mode on non tty input"
I don't know how to solve this problem.
Issue 10734 mentions:
The java process is not providing a TTY to the docker CLI but you've asked for a TTY by specifying -t in your command.
So, does the image actually need a TTY? If not, don't specifiy -t... if the image doesn't need stdin at all don't specify -i either.
If it does need the TTY, then you'll need to setup the TTY for your command and it should work.
For example, see "persistent local tty session with java"
Instead of Runtime.getRuntime().exec("command"); do Runtime.getRuntime().exec("/bin/sh"); and hold on to the Process object
"Runtime.exec with Unix console programs" illustrates that in the context of using less:
Process p = Runtime.getRuntime().exec(new String[] {"sh", "-c",
"less >/dev/tty"});
OutputStream out = p.getOutputStream();
out.write("Lengthy message".getBytes());
out.close();
System.out.println("=> "+p.waitFor());

How to execute unix commands through Windows/cygwin using Java

I am trying to accomplish two things:
I am running cygwin on Windows7 to execute my unix shell commands and I need to automate the process by writing a Java app. I already know how to use the windows shell through Java using the 'Process class' and Runtime.getRuntime().exec("cmd /c dir"). I need to be able to do the same with unix commands: i.e.: ls -la and so forth. What should I look into?
Is there a way to remember a shell's state?
explanation: when I use: Runtime.getRuntime().exec("cmd /c dir"), I always get a listing of my home directory. If I do Runtime.getRuntime().exec("cmd /c cd <some-folder>") and then do Runtime.getRuntime().exec("cmd /c dir") again, I will still get the listing of my home folder. Is there a way to tell the process to remember its state, like a regular shell would?
It seems that the bash command line proposed by PaĆ­lo does not work:
C:\cygwin\bin>bash -c ls -la
-la: ls: command not found
I am having trouble figuring out the technicalities.
This is my code:
p = Runtime.getRuntime().exec("C:\\cygwin\\bin\\bash.exe -c ls -la");
reader2 = new BufferedReader(new InputStreamReader(p.getInputStream()));
line = reader2.readLine();
line ends up having a null value.
I added this to my .bash_profile:
#BASH
export BASH_HOME=/cygdrive/c/cygwin
export PATH=$BASH_HOME/bin:$PATH
I added the following as well:
System Properties -> advanced -> Environment variables -> user variebales -> variable: BASH, value: c:\cygwin\bin
Still nothing...
However, if I execute this instead, it works!
p = Runtime.getRuntime().exec("c:\\cygwin\\bin\\ls -la ~/\"Eclipse_Workspace/RenameScript/files copy\"");
1. Calling unix commands:
You simply need to call your unix shell (e.g. the bash delivered with cygwin) instead of cmd.
bash -c "ls -la"
should do. Of course, if your command is an external program, you could simply call it directly:
ls -la
When starting this from Java, it is best to use the variant which takes a string array, as then
you don't have Java let it parse to see where the arguments start and stop:
Process p =
Runtime.getRuntime().exec(new String[]{"C:\\cygwin\\bin\\bash.exe",
"-c", "ls -la"},
new String[]{"PATH=/cygdrive/c/cygwin/bin"});
The error message in your example (ls: command not found) seems to show that your bash can't find the ls command. Maybe you need to put it into the PATH variable (see above for a way to do this from Java).
Maybe instead of /cygdrive/c/cygwin/bin, the right directory name would be /usr/bin.
(Everything is a bit complicated here by having to bridge between Unix and Windows
conventions everywhere.)
The simple ls command can be called like this:
Process p = Runtime.getRuntime().exec(new String[]{"C:\\cygwin\\bin\\ls.exe", "-la"});
2. Invoking multiple commands:
There are basically two ways of invoking multiple commands in one shell:
passing them all at once to the shell; or
passing them interactively to the shell.
For the first way, simply give multiple commands as argument to the -c option, separated by ; or \n (a newline), like this:
bash -c "cd /bin/ ; ls -la"
or from Java (adapting the example above):
Process p =
Runtime.getRuntime().exec(new String[]{"C:\\cygwin\\bin\\bash.exe",
"-c", "cd /bin/; ls -la"},
new String[]{"PATH=/cygdrive/c/cygwin/bin"});
Here the shell will parse the command line as, and execute it as a script. If it contains multiple commands, they will all be executed, if the shell does not somehow exit before for some reason (like an exit command). (I'm not sure if the Windows cmd does work in a similar way. Please test and report.)
Instead of passing the bash (or cmd or whatever shell you are using) the commands on the
command line, you can pass them via the Process' input stream.
A shell started in "input mode" (e.g. one which got neither the -c option nor a shell script file argument) will read input from the stream, and interpret the first line as a command (or several ones).
Then it will execute this command. The command itself might read more input from the stream, if it wants.
Then the shell will read the next line, interpret it as a command, and execute.
(In some cases the shell has to read more than one line, for example for long strings or composed commands like if or loops.)
This will go on until either the end of the stream (e.g. stream.close() at your side) or executing an explicit exit command (or some other reasons to exit).
Here would be an example for this:
Process p = Runtime.getRuntime().exec(new String[]{"C:\\cygwin\\bin\\bash.exe", "-s"});
InputStream outStream = p.getInputStream(); // normal output of the shell
InputStream errStream = p.getInputStream(); // error output of the shell
// TODO: start separate threads to read these streams
PrintStream ps = new PrintStream(p.getOutputStream());
ps.println("cd /bin/");
ps.println("ls -la");
ps.println("exit");
ps.close();
You do not need cygwin here. There are several pure Java libraries implementing SSH protocol. Use them. BTW they will solve your second problem. You will open session and execute command withing the same session, so the shell state will be preserved automatically.
One example would be JSch.

Java Runtime.exec running java problem

I have a simple server application, which I would like to run in the background. The following line works for me:
Runtime.getRuntime().exec("cmd /c start java -jar ..\\server\\server.jar -Dlog4j.configuration=file:src\\test\\resources\\log4j.properties -filename src\\test\\resources\\server.properties");
But it displays the cmd window and I am unable to destroy it. So I would like to use
Runtime.getRuntime().exec("java -jar ..\\server\\server.jar -Dlog4j.configuration=file:src\\test\\resources\\log4j.properties -filename src\\test\\resources\\scIntegration.properties");
But it simply doesn't connect to the server. So why is that?
A related question. How do I end the process? It is a server that "doesn't end". So I have to kill it and I would assume, that running the java only command would be capable to be destroyed, but with the cmd I have no luck there.
You should split your command into an array in which first argument is the actual command to run and all the rest are command like arguments:
Runtime.getRuntime().exec(new String[] {"/usr/bin/java", "-jar", "..\\server\\server.jar" ...});
Try using an absolute path to the java programm.
For destroying: exec() returns a java.lang.Process, which you should be able to destroy. If not, you have to implement some type of callback to shut your server down, e.g. listening on a specific prot for a shutdown command.
The server is outputing something to stdout and in the shortened command version it didn't have a place to output, so it got stuck while trying to output some data. The solution is to pipe the stdout to eg some file.

Categories