I am trying to run the below command from java code using Process process =Runtime.getRuntime().exec(command) but getting the error.
Command: repo forall -c 'pwd;git status'
Error:'pwd;git: -c: line 0: unexpected EOF while looking for matching''`
I am able to run this command from linux terminal but when running from java the problem is with the space after pwd;git. Can anyone help me?
This is an ultra classical mistake and I am frankly surprised that you didn't find the answer to it by searching around.
A Process is not a command interpreter.
However, Runtime.exec() will still try and act as one if you pass it only one argument, and here you'll end up splitting like this:
repo
forall
-c
'pwd;git
status'
Which is obviously not what you want.
Use a ProcessBuilder. I won't do it all for you but here is how to start:
final Process p = new ProcessBuilder()
.command("repo", "forall", "-c", "pwd; git status")
// etc etc
.start();
Link to the javadoc.
Related
I'm having the next error when execute a cmd command using Java. I'm working in a mac laptop. This is my code:
private static String exportContainerFromImage(String container) {
//docker export mysql_dummy > ~/Documents/mysql_dummy.tar
String errorMessage = "";
String[] cmdArgs =
{"docker export mysql_dummy > ~/Documents/mysql_dummy.tar", "bash"};
Process process = Runtime.getRuntime().exec(cmdArgs);
}
But I'm getting the error, error=2, No such file or directory, if I execute the command directly on the terminal it runs successfully, I tried also changing the directory to ~\\Documents\\mysql_dummy.tar and got the same result.
But if I run the command with the arguments:
{"docker create -ti --name mysql_dummy mysql", "bash"};
It runs properly
Any ideas?
You're conflating 'shell magic' with 'an OS'. Also, you seem to be wildly confused about what the array form of cmdArgs does, because you've tagged a bash in there at the end. That array is supposed to contain the executable's full path at arr[0], and all arguments at arr[1] and up. docker create -ti ... is clearly not a filename, and bash is clearly not an argument.
Shell magic?
If you type:
docker create -ti --name mysql_dummy mysql
on the command line, bash (or cmd.exe if on windows, or whatever shell you are using) reads it and does a whole bunch of replacement magic and parsing on this. It's the shell that does this, not the OS, and java's processbuilder stuff is not a shell and therefore isn't going to do all that. What you're attempting to do? Run that entire line as if it's a single file name that is executable which it clearly isn't.
This is all shell magic - all things that you CANNOT do with exec. Fortunately, java is a programming language, so you can do all these things by, well, programming it.
Parsing out params by splitting on whitespace.
quoting to avoid that splitting, but then removing the quotes.
Treating ~ as a ref to a homedir.
Replacing * and ? in filename paths.
Variable substitution
Setting up redirects with > somefile.txt or 2> /dev/null or < file.in or whatnot.
You must do those things.
In addition, exec cannot be used to this, period. As usual, the only non-problematic way to run processes is to always use ProcessBuilder, no exceptions. Consider runtime.exec a known-broken method you must never call.
ProcessBuilder lets you redirect the output.
String[] cmdArgs = {
"/bin/docker" // note, FULL PATH!
"export",
"mysql_dummy"
};
File out = new File(System.getProperty("user.home"), "Documents/mysql_dummy.tar");
ProcessBuilder pb = new ProcessBuilder(cmdArgs);
pb.redirectOutput(new File(out));
pb.start();
That does what you want, presumably.
The alternative is to make a script (script.sh or script.bat) and then start bash or cmd.exe and ask it to run that script.
String[] args = { "/bin/bash", "-c", "/fully/qualified/path/to/the/script.sh" }
and then exec that. Now you can pile *.txt, > foobar.txt, ~/homediref, and all the other shellisms in that script as much as you like.
Overall #rzwiterloot's answer is good, but there are some alternatives it leaves out.
First, what I would consider the best solution to this problem: the -o option to docker export.
"docker", "export", "mysql_dummy", "-o", "Documents/mysql_dummy.tar"
Ignoring ~/ here, this set of command and arguments will achieve the same thing as ... > Documents/mysql_dummy.tar but doesn't rely on the shell for the redirection; docker export is perfectly capable of handling that operation itself.
Second, if you wanted to run a shell command from the program, you could. I would not recommend this. But in certain circumstances it might make sense.
The alternative is to make a script
You don't have to put the command in a separate file. Actually this is one inaccuracy in #rzwiterloot's answer; -c allows you to pass command(s) to bash, not the path to a file containing commands.
"bash", "-c", "docker export mysql_dummy > ~/Documents/mysql_dummy.tar"
However, I'd recommend you avoid invoking shells from any program you write. They're quirky and esoteric and there's almost always a simpler way to achieve what you want, such as docker export's -o optiopn, in this case.
I want to execute wsl commands from java.
I'm trying to do this using Process and ProcessBuilder.
As I understand, there are two ways to do this:
Run wsl along with command as argument (for example: wsl ls -l) (do this per-command).
Run wsl, and then execute the commands one by one.
But there are some problems with 1 and 2.
With point 1:
When the command terminates, the process does not stop. So even if i run wsl ls, I can not determine the moment when I can call next command.
wsl does not save the state between such calls, so it's not very convenient
With point 2:
Since wsl does not show bash prompt, I can not track when the command stopped displaying information. For example:
ProcessBuilder pb = new ProcessBuilder("wsl");
pb.redirectErrorStream(true);
Process p = pb.start();
Thread.sleep(1000);
OutputStreamWriter osw = new OutputStreamWriter(p.getOutputStream());
osw.write("ls\n");
osw.flush();
And all I can read is:
build.gradle
gradle
gradlew
gradlew.bat
out
settings.gradle
src
No selya#selya-pc:/mnt/c/Users/selya$ read. So I can't use it as a separator between commands. I think that wsl somehow tracks, in what environment it was launched, for example, through isatty(), and therefore the output is different.
With both:
Аs far as I know, for programs that are running outside the terminal, stdout is not buffered. So there are some problems with, for example, sudo, because it asks fro a password without newline/flush ([sudo] password for selya:), therefore I can't read this line...
I found a solution - pass command as argument to unbuffer util, for example:
wsl unbuffer -p sudo apt-get update
(-p stands for pipeline).
But other problems still remain. Is there any way to do this? Or maybe there is lib for it? Even c or c++ lib will suit my needs...
P.S. I tried to find a solution for several days. Russian-speaking SO didn't help me, that's why I'm here. Sorry for my English...
The problem was solved with the help of pty4j. It works on linux, mac and windows. (It works like pseudo terminal on linux, but have nice java interface).
QUESTION: What does each element of the command:
pkill -INT -f '^php test_program.php$'
do when I run it in the linux terminal? I already know that the command kills the process called test_program.php, but I don't know what all the different elements of the command are doing. Please explain in as simple terminology as possible! I am new to linux commands and I prefer baby lingo to tech lingo at the moment :)
MY RESEARCH: By running man pkill in the linux terminal, a manual appears with the following pkill definition:
signal processs based on their name or other attributes.
which leads me to believe that pkill doesn't only kill a process, but rather can send a lot of different signals, one of which might kill the process. The structure/synopsis of the pkill command was displayed as: pkill [option] pattern
From the list of options in the same manual, -f, -full had the following definition:
The pattern is normally only matched against the process name. When -f is set, the full command line is used.
I didn't completely understand what that meant. Also, there is a -INT before the -f in the command, so that leads me to believe that more than one option can be joined together, however -INT was not displayed in the manual.
The other parts of the command seem to be identifying the program that is running: '^php test_program.php$', but why isn't that part of the command just 'test_program.php'? What does ^php at the beginning and $ and the end do?
You are looking at a Regular Expression. This expression looks for the string test_program.php anywhere in the process name. So if the process name would be something like
/var/php -runcommand test_program.php
it would find the process and kill it.
This also explains the -f, -full option. Not using a Regular Expression, you would have to take the full process name (the preceeding line) to match the process.
Finally, the -INT is usually used to send a runlevel to the task.
EDIT
I was wrong, the -INT option is not used for runlevels (which are for the Linux kernel) but to send signals to a task. This could be something like Term (terminate), Stop (shut down) or Cont (continue process). pkill sends by default the terminate signal to the process.
The /var/php -runcommand test_program.php was an example for a process. If you use the command ps ax, you get a list of all processes and which programs execute them. So I just assumed that the php interpreter resides in /var/php/ and the execution of the php file is a command.
This is an extremely strange situation, but I just cannot point out what I'm doing wrong.
I'm executing a big bunch of SQL scripts (table creation scripts, mostly). They are executed through Java, using sqlcmd. Here's the sqlcmd command I use.
sqlcmd -m 11 -S SERVER -d DB -U USER -P PASS -r0 -i "SCRIPT.sql" 2> "ERRORS.log" 1> NULL
Note: I use the -r0 and redirects to make sure only errors go into the log file. I chuck out all STDOUTs.
Now I execute this command in Java, using getRuntime.exec(), like this.
Runtime.getRuntime().gc();
strCmd = "cmd /c sqlcmd -m 11 -S SERVER -d DB -U USER -P PASS -r0 -i \"SCRIPT.sql\" 2> \"ERRORS.log\" 1> NULL"
Process proc = Runtime.getRuntime().exec(strCmd);
proc.waitFor();
Note: I use cmd /c, so that the command runs in its own shell and exits gracefully. Also, this helps in immediately reading the error log to look for errors.
The Problem!
This command works perfectly when run by hand on the command prompt (i.e. the tables are getting created as intended). However, when executed through Java as shown, the scripts are run, and and there are no errors, no exceptions, nothing in the logs. But, when checking in SSMS, the tables aren't there!
Where do I even begin debugging this issue?
UPDATE: I'M A MORON
The return value from the getRuntime().exec method is 1. It should be 0, which denotes normal execution.
Any pointers on how to fix this?
UPDATE 2
I've looked at the process' ErrorStream, and this is what it has.
Sqlcmd: Error: Error occurred while opening or operating on file 2>
(Reason: The filename, directory name, or volume label syntax is
incorrect).
Looks like the path I'm passing is wrong. The error log goes into my profile directory, which is C:\Documents and Settings\my_username. Do the spaces in the path matter? I'm anyways double-quoting them!
Have a look at the exec method with an string array as parameter:
java.lang.Runtime.exec(String[] cmdArray)
The JavaDoc for this method says:
Executes the specified command and arguments in a separate process.
So, the first item in the array is the command and all of your arguments are appended to the array, e. g.,
Runtime.getRuntime().exec(new String[] {"cmd", "/c", "sqlcmd ... "});
After looking at your comment and the implementation of exec(String) it seems to be, that the exec method recognizes the pipe operator > as an argument to cmd, because exec(String) splits the command string to an array using whitespaces as seperators.
I don't have privs to post comments - which is what this is - but what if you try putting in a bogus user id for the DB? Does that cause a different execution path? Will that give you a Java error? Or an Auth error in your DB? Also, def tweak the user, not the password and learn from my experience that if you tweak the password that's a great way to get an account locked out!
The other thing - and this may be a shot in the dark - but what are the JRE and driver you're using? I believe there's a known issue with JRE 1.6.0.29 and the sqljdbc4 JAR. I have more details on this, but I'll have to post the link once I get to work.
Edit:
I know it's been established that the JRE/sqljdbc combo isn't your issue, but if folks search and find this, here is the link I spoke of above:
Driver.getConnection hangs using SQLServer driver and Java 1.6.0_29
First enable log/view commands output (since exec() returns 1), which would point out possible cause of the issue.
Use proc.getInputStream() and print the contents to a file or console.
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.