Java Runtime OR Processbuilder OR Other - java

I'd like to know what the best alternative is for running command line executables from Java. The Target platforms for the commands are Windows 7(+) and Unix/Linux.
I have a class that currently uses Runtime.exec() along with the enhancements from the JavaWorld StreamGobbler article. It works about 90% of the time on both Windows and Unix. The other 10% of the time I need to extend the class and then fiddle with putting cmd.exe of /bin/sh in front of the command. I've also had to fiddle sometimes between using a single String that has command and arguments to splitting the command and args into a String[] array.
My latest is a new error/exception "java.lang.IllegalArgumentException: Executable name has embedded quote, split the arguments." My current Runtime.exec() class works fine in Eclipse running as a Java application, but once I build it and run from an actually command prompt, it fails with the above exception.
So now I'm reading that we should be using ProcessBuilder to do command line executables to the OS platform.
My question is, what is the best alternative? Runtime.exec(), ProcessBuilder, or some other option? Is there one option that will service both Windows and Unix/Linux? If not, which one works best with Windows? Which one works best with Unix/Linux?
tia, adym

Not sure how to give Bohemian credit, but I used ProcessBuilder...Solution is at :
Java - ProcessBuilder command arguments with spaces and double-quotes fails

Related

Shell command in Java with Runtime.getRuntime().exec();

Is this a simple and good way to execute a Shell command via Java?
Runtime.getRuntime().exec( some command );
Or is this bad practice?
It depends.
The original purpose and basic functionality of a Unix shell is to let you run programs, optionally passing them arguments. For example the command ls runs the ls program, and the command grep foo bar runs the grep program with the arguments foo and bar. If your command only runs a (fixed) program with fixed if any arguments, Runtime.exec can do it. There are two subcases:
the overloads taking a String parse the line into 'words' (program name and arguments) using any whitespace; this is essentially the same as the default parsing (with no quoting) done by standard shells.
if you need any different parsing, for example if your command would use any quoting in shell, you must do that parsing yourself and pass the results to one of the overloads taking a String[].
But note that when you run a program from an interactive shell -- one using a terminal or equivalent (sometimes called a console) for input and output -- the program's input and output default to that terminal. The I/O for a program run by Runtime.exec is always pipes from and to the Java process, and some programs behave differently when their input and/or output is/are pipe(s) -- or file(s) -- instead of a terminal. Plus you must write code to send (write) any desired input and receive (read) any output. Of course, shells can be and sometimes are run without a terminal too.
However, shells can be and routinely are used to do much much more than the basics:
shell can execute commands with contents different from the input by variable (formally parameter) substitution (possibly with modification/editing), command substitution, process substitution, special notations like squiggle and bang, and filename expansion aka 'globbing' (so called because in the early versions of Unix it was done by a separate program named glob). Runtime.exec doesn't do these, although you can write Java code to produce the same resulting command execution by very different means.
shell executes some commands directly in the shell rather than by running a program, because these commands affect the shell process itself,
like cd umask ulimit exec source/. eval exit alias/unalias, or variables in the shell like set shift unset export local readonly declare typeset let read readarray/mapfile,
or child process like jobs fg bg, or special parsing like [[ ]] and (( )) (in some shells). These are called 'builtin' and Runtime.exec can't do them,
with two partial exceptions: it can run a program with a different working directory and/or env var settings, equivalent to having previously executed cd or export or equivalent.
Shell also often has builtins that duplicate, or modify, a 'normal' program; these commonly include test/[ echo printf kill time. Runtime.exec can only do the program version, not the builtin version.
shell has control structures (compound commands) like if/then/else/elif/fi and while/for/do/done and trap && || ( ) { }. Runtime.exec can't do these, although in some cases you could use Java logic to produce the same results.
shell can also have user-defined functions and aliases that can be used as commands; Runtime.exec does not.
shell can redirect the I/O of programs it runs, including forming pipes. Runtime.exec can't do these, but see below.
Since 1.5, Java also has ProcessBuilder, which provides the same functionality and more, in a more flexible and arguably clearer API, and thus is generally recommended instead. ProcessBuilder does support redirecting I/O for the program it runs, including using the terminal/console if the JVM was run on/from one (which is not always the case), and since 9 it can build a pipeline. It does not have the word-splitting functionality of Runtime.exec(String) but you can easily get the same result with string.split("[ \t]+") or in most cases just " +".
Note shell is itself a program, so you can use either Runtime.exec or ProcessBuilder to run a shell and pass it a command, either as an argument using option -c (on standard shells at least) or as input, and unsurprisingly this shell command can do anything a shell command can do.
But this can be a portability issue because different systems may have different shells, although any system claiming Unix certification or POSIX conformance must have a shell named sh that meets certain minimum requirements.
The actual shell used on different systems might be any of bash dash ksh ash or even more. OTOH this is true for other programs as well; some programs that typically differ significantly on different systems are awk sed grep and anything to do with administration like netstat.
A few of the existing Qs that show shell commands that don't work in Runtime.exec at least as-is:
a command for sherlock.py is interpreted differently from linux command line and java process api
Execute shell script multiple commands in one line using Process Builder in Java (Unix)
Check in Java if a certain application is in focus
Problem in executing command on AIX through Java
ProcessBuilder doesn't recognise embedded command
File not Found when executing a python scipt from java
Java system command to load sqlite3 db from file fails
Curl To Download Image In JAVA
Keytool command does not work when invoked with Java
use javap from within a java program on all the files
Using SSMTP and ProcessBuilder
Process Builder Arguments
Whitespace in bash path with java
java.lang.Runtime exception "Cannot run program"
Why does Runtime.exec(String) work for some but not all commands?
How to save Top command output in a text or csv file in java?
Execute bash-command in Java won't give a return
Using Java's Runtime.getRuntime().exec I get error with some commands, success with others -- how can this be determined?
Java and exec command - pipe multiple commands
Java exec() does not return expected result of pipes' connected commands
How to make pipes work with Runtime.exec()?
How to use Pipe Symbol through exec in Java
In Runtime.getRuntime().exec() getting error: /bin/bash: No such file or directory
Java exec linux command
How to use pipes in a java Runtime.exec
Java Runtime.getRuntime().exec and the vertical bar
Whenever I execute terminal command from code it gives "cannot run program" error=2 No such file or directory
Command line proccess read linux in java
Java Command line system call does not work properly

How to use Java to run a command through Mac OS Terminal

I am a high school student working on a project that will convert the video from a YouTube link into an MP3 and download it. However, the way that the video form YouTube is downloaded and converted is through the Mac OS terminal by using YouTube-dl.
This is what I put into the terminal:
youtube-dl -f bestvideo+bestaudio \"ytsearch:{https://www.youtube.com/watch?v=5X-Mrc2l1d0}\"
This works in terminal, but when I try to use:
Runtime.getRuntime().exec("cd /Users/poppa/Desktop/IA Vids");
and there's an error saying "No such file or directory"
Another Problem that I am having is running the code that is inputted into the Terminal from Java. I'm using IntelliJ IDEA if that helps. Thank You!
You have a space in the directory path. Try putting double quotes around like this:
Runtime.getRuntime().exec("cd \"/Users/zeidakel/Desktop/IA Vids\"");
Also note that executing cd command from JVM may have no effect on current user dir when (for example) creating files with new File("path")
If cd means change directory (and isn't the name of an executable), then it almost certainly won't take effect, even if it is executed correctly. The process spawned by exec() will have a working directory, and it can be changed -- but that change will only affect the spawned process.
In addition, having spaces in arguments to exec() is inherently problematic. exec() is not a shell, and you won't be able to protect the string from being split at the spaces by using shell mechanisms like quotes. Instead, you need to split the command into arguments yourself, knowing where the splits should be, and then use the form of exec() that takes a String[] as input. That is, split the arguments into an array of strings yourself, rather than relying on exec() to do it for you (wrongly).
Runtime.exec() is fraught with difficulties, and needs very careful handling. I've written extensively about this subject here:
http://kevinboone.me/exec.html

How do I treat whitespace on SSH command sent via Scala/Java ProcessBuilder?

The Scala ProcessBuilder uses the underlying Java ProcessBuilder, which has its own whitespace treatment routine that breaks SSH commands that otherwise work on the shell. I'm trying to get the Perl backticks and system() behavior where the interpreter runs an underlying shell and executes the shell command. Is there something similar for Java/Scala?
I'm trying this, specifically:
ssh REMOTE_SERVER '/usr/bin/tail -n 25 /var/log/messages'
Where /usr/bin/tail is the right path to the command, double checked it.
To get the latest log messages from a server. It works fine on the shell, but it breaks on the ProcessBuilder with No Such file or directory. Obviously ProcessBuilder quoted or escaped the command somehow and broke it.
Thanks in advance for any ideas.
Turns out I need to put the whole remote command into the last parameter of a Seq and hope ProcessBuilder guesses the right quotes to use.
import scala.sys.process._
// .....
Seq("/usr/bin/ssh","REMOTE_HOST","/usr/bin/tail -n 25 /var/log/messages")
Very little control offered over how it treats that whole last parameter. Anyway, this works. If anyone knows of a library that works more like the Perl backtick or system() behavior I'd really appreciate it (no problem if it's not 100% portable, this will only run under Linux). Thanks.

Where does Java's ProcessBuilder look to execute commands?

When I execute a command using ProcessBuilder, how does it know where to look for that command? Using this hack/trick I've modified my PATH variable (verified by inspecting processBuilder.environment()) to be bad (empty, working dir, etc) but ProcessBuilder can still execute sort, echo, bash, etc. just fine. How is it doing this?!
Note: My particular development environment is OSX but this code will also run on Red Hat Enterprise Linux.
The documentation says
[...] a command, a list of strings which signifies the external program file to be invoked and its arguments, if any. Which string lists represent a valid operating system command is system-dependent. [...]
Which in essence mean that where it looks for programs to execute depends on the particular system and JVM you're running on.
I can't find a complete matrix of JVM / System behaviors, but supposedly it behaves similar to the popular shells of the system (bash for *nix and cmd for windows) i.e. it searches the directories in the PATH environment variable from left to right and executes the first executable file it finds.
If you want to take control of finding commands, then, well, take control of finding commands. Don't let ProcessBuilder search. Use your own code to find what you want to run, and then put an absolute pathname into the parameter to ProcessBuilder.

Launching jEdit From Java Swing App Using Runtime.getRuntime().exec()

I'm writing a Java Swing Application running on Red Hat Enterprise Linux 5 server that I would like to launch jEdit to view log files.
Here is some example code.
public static void main(String[] args) throws IOException, InterruptedException {
String cmd = "sh -c \"java -jar /tmp/jEdit/jedit.jar /tmp/test.txt\"";
System.out.println(cmd);
Runtime.getRuntime().exec(cmd);
}
The output is:
sh -c "java -jar /tmp/jEdit/jedit.jar /tmp/test.txt"
If I copy and paste the cmd output in a terminal window, it runs fine.
I have tried a bunch of cmd values, but I can never get the jEdit window to be visible.
With changes, this process works fine on Windows.
Is what I'm doing possible on Linux?
Thanks in advance!
As jEdit is implemented in Java, perhaps it would be easier to check the source for what the main method (in the class declared in the manifest file included in the jedit.jar) does and do the same thing without using Runtime.getRuntime().exec() at all.
If you do want to stick with it, you could try passing the individual commands as an array to exec(), this often solved such problems for me.
Linux uses the concept of display ports for its X-Windows system. This allows it to maintain a different desktop environment for each user. It also allows a user on remote machine to run a desktop app from the first machine but see the UI on the remote.
Windows, having only one available desktop environment at a time, does not.
First thing you definitely have to do is add the environment variable "DISPLAY=localhost:0" to the environment from which you are launching this. However, you may also need to run 'xhost +localhost' or this may not be allowed.
Double-check, too, that you didn't successfully launch a bunch of jEdit processes that are now zombies (using top) and kill them if necessary (using kill).
Runtime.exec() needs some special attention. The exec method that accepts a String uses the space character as a delimiter to break up the string into commands. You need to use the exec method that accepts a String[]. Read more here, specifically near the bottom.
I´ve done this once and I got the same problem
What I've done is to write the command line into a text file and then execute the text file as a shell script file.
It worked fine for me.
Jedit has a launcher script, /usr/bin/jedit I guess. Simply typing jedit in command prompt runs it, at least in current version, 4.5. Try that script instead of explicit java command.

Categories

Resources