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.
Related
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)
I want to start/stop a service from my java program for which I am using terminal commands. The problem is that I need to start the terminal as admin for which I am using the ProcessBuilder API.
public void startService(String serviceName) throws IOException, InterruptedException {
String[] cmdArray = {"cmd.exe", "/c",
"runas /savecred /profile /user:admin \"sc start " + serviceName + "\""};
Process process = new ProcessBuilder(cmdArray).start();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
OutputStreamWriter output = new OutputStreamWriter(process.getOutputStream());
String line;
while ((line = bufferedReader.readLine()) != null) {
System.out.println(line); //Prints "enter the password for admin"
}
output.write("password"); //my password
output.newLine();
output.flush();
//output.wait();
process.destroy();
}
The command executes fine and it prints Enter the password for admin but when I supply it through the output stream of process, I get the I/O error java.io.IOException: The pipe is being closed. I also tried to add echo mypassword | before the command which does not prompt for password on terminal but is prompting from java program. Any help on this is much appreciated.
EDIT- I am getting the error on output.flush()
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 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();