I am using this method to send commands to the linux terminal
public static String execute(String command) {
StringBuilder sb = new StringBuilder();
String[] commands = new String[]{"/bin/sh", "-c", command};
try {
Process proc = new ProcessBuilder(commands).start();
BufferedReader stdInput = new BufferedReader(new InputStreamReader(proc.getInputStream()));
BufferedReader stdError = new BufferedReader(new InputStreamReader(proc.getErrorStream()));
String s = null;
while ((s = stdInput.readLine()) != null) {
sb.append(s);
sb.append("\n");
}
while ((s = stdError.readLine()) != null) {
sb.append(s);
sb.append("\n");
}
} catch (IOException e) {
e.printStackTrace();
}
return sb.toString();
}
It works fine with many commands like "ls", "date" etc..
but I wanna use a psql command to import a sql file to postgre like:
psql -h localhost -p 5432 -U postgres -d test -f test.sql
I typed this command by hand in terminal and it works fine but not with the method above (the method works well with date,ls...)
It looks like the method entries in a kind of infinite loop when it calls psql. The method does not end when the method calls the psql.
ps: I used export PGPASSWORD=pass to avoid pass the password.
I think your problem here might be that you are trying to consume the input and error streams sequentially, when you must actually do them simultaneously. Consequently your first readLine call is blocking, waiting for the readLine to finish on the other stream.
You can solve this by using multiple threads to read the streams, or by redirecting stderr, or by just ditching all the output. Have a look at these threads:
ProcessBuilder: Forwarding stdout and stderr of started processes without blocking the main thread
Java ProcessBuilder: Resultant Process Hangs
I solved the problem creating a file file.sh with
export PGPASSWORD=pass
psql -h ip -p port -U user -d databaseName -f sqlFilePath
unset PGPASSWORD
then I Use the method execute:
execute("chmod +x file.sh"); //to give permission to file execute
execute ("./file.sh") //to execute the file in terminal
in the end I delete the file.sh with
File file = new File("file.sh");
file.delete();
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 trying to run a shell script from Java (using Runtime.getRuntime().exec(cmd)). All commands in the script file seem to be running normally except the angular-cli (ng) commands.
My Java File:
System.out.println("Executing Script...");
final String[] cmd = new String[]{"/bin/bash", "test.sh"};
final Process process = Runtime.getRuntime().exec(cmd);
process.waitFor();
final BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String s;
while ((s = reader.readLine()) != null) {
System.out.println("Script output: " + s);
}
process.destroy();
System.out.println("Script Executed.");
test.sh:
#!/bin/bash
cd ~/ &&
ng new newAngularProject &&
Outout:
Executing Script...
Script Executed.
No errors are thrown. All other commands work but for some reason, I'm unable to run ng commands. Also, I've tested the file w/o running it from Java - When I run the same script directly on the console, it works perfectly and all commands (including ng commands) work neatly. I'm running on MacOS in case you wanted to know.
Also print the error stream. You will get the error message, if it is there.
final BufferedReader errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
while ((s = errorReader.readLine()) != null) {
System.out.println("error: " + s);
}
Also you can try to use absolute path of ng in your test.sh e.g. /home/my/install/node-vxxx/ng, since the process spawn by java to run your command might not get the environment variable you set in your .bashrc /.bash_aliases
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 have a Java-App, which should execute an sh command.
My command looks like sudo /bin/sh -c "echo 7 > /sys/class/gpio/export" and when I execute this in the command prompt of my computer it works, but not with my Java-Programm.
The Programm-line looks like this:
System.out.println(CmdExecutor.execute("sudo /bin/sh -c \"echo 7 > /sys/class/gpio/export\""));
public class CmdExecutor {
public static String execute(String[] cmd) {
StringBuffer output = new StringBuffer();
Process p;
try {
p = Runtime.getRuntime().exec(cmd);
p.waitFor();
BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line = "";
while ((line = reader.readLine()) != null) {
output.append(line).append("\n");
}
} catch (IOException | InterruptedException e) {
}
return output.toString();
}
public static String execute(String cmd) {
StringBuffer output = new StringBuffer();
Process p;
try {
p = Runtime.getRuntime().exec(cmd);
p.waitFor();
BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line = "";
while ((line = reader.readLine()) != null) {
output.append(line).append("\n");
}
} catch (IOException | InterruptedException e) {
}
return output.toString();
}
}
Can someone help me?
I see two issues:
Multiple arguments need to be split in Java already.
Authentication with sudo.
Multiple arguments need to be split.
If you run exec("a b"), the system will look for a command named a b as one single String command name.
If you run exec("a", "b"), the system will look for a command namedaand passb` as argument to that program.
So what you want to do is execute("sudo", "/bin/sh", "-c", "echo 7 > /sys/class/gpio/export").
sudo might require authentication
When you execute commands with sudo, an authentication will be performed. If you execute multiple sudo commands from the same process, the system will cache the authentication for convenience, but basically the authentication is required.
The authentication with sudo usually means that you need to supply a password.
The reason why you sudo this is that /sys/class/gpio/export has permissions -w------- (200) owned by root root, which means nobody can read it and only root can write it.
You have a few options:
Change the permissions of that file so that everybody can write it (not recommended): chmod a+w /sys/class/gpio/export.
Change the permissions of that file so that the user in question can write it: setfacl -m user:cher:w /sys/class/gpio/export - note that this only works if your sysfs is mounted with acl option, and usually it isn't. I don't know if it's even possible to mount sysfs with acl option, I haven't tried myself.
Pipe the password to the sudo command: exec("echo password | sudo /bin/sh -c \"echo 7 > /sys/class/gpio/export\"") WARNING THIS IS DANGEROUS!!!
Use a graphical sudo replacement like kdesudo
Change your sudoers configuration so that the user in question never needs to enter password for sudo - not recommended.
I need to create a method that receives (databaseIp, databaseName, port, user, password, filePath). The idea is to import a sql file in database like terminal:
psql -h databaseIp -p port -U user -d databaseName
Password for user postgres: password
databaseName=# \\i filePath
databaseName=# \\q
I use the following method to send commands to the terminal
public static String execute(String command) {
StringBuilder sb = new StringBuilder();
String[] commands = new String[]{"/bin/sh", "-c", command};
try {
Process proc = new ProcessBuilder(commands).start();
BufferedReader stdInput = new BufferedReader(new InputStreamReader(proc.getInputStream()));
BufferedReader stdError = new BufferedReader(new InputStreamReader(proc.getErrorStream()));
String s = null;
while ((s = stdInput.readLine()) != null) {
sb.append(s);
sb.append("\n");
}
while ((s = stdError.readLine()) != null) {
sb.append(s);
sb.append("\n");
}
} catch (IOException e) {
e.printStackTrace();
}
return sb.toString();
}
but It seens that it sends only one command at a time and when the psql asks for passwords the result given is incorrect. How can I send related commands to the terminal using java?
Ugh, why would you want to do this?
Use PgJDBC to communicate with PostgreSQL from Java.
If you really must invoke psql, say if you're sourcing an SQL file that uses \ commands, build a command with ProcessBuilder and invoke psql non-interactively in batch mode. You can prompt the user for their password and then pass it to psql in the PGPASSWORD environment variable so you never need to interact with the process, you just launch it and wait for it to terminate.
Something like the (untested):
cmd = new List<String>();
cmd.add(path_to_psql);
cmd.add("-q"); // Quiet mode
cmd.add("-w"); // no password prompt
cmd.add("-X"); // ignore any user psqlrc
cmd.add("-1"); // Run in a single transaction
cmd.add("-v"); // Set a variable as follows:
cmd.add("ON_ERROR_STOP=1"); // If anything goes wrong, abort the whole job
cmd.add("-f"); // input file is:
cmd.add(input_file_path);
if (username_arg != null) {
cmd.add("-U");
cmd.add(username_arg);
}
// and so on for database name, etc, then:
ProcessBuilder pb = new ProcessBuilder(cmd);
Map<String, String> env = pb.environment();
if (psql_password != null) {
// User supplied a password
env.put("PGPASSWORD", psql_password);
}
// ... blah blah, the usual execute it, wait for it to finish,
// check return value, etc.
If psql fails to connect the first time with error code 2 you can prompt the user to enter their password and re-try. Unfortunately error code 2 isn't very specific; it will also be returned for errors related to invalid host names, etc, so you might want to capture psql's stderr and display that to the user along with your prompt.