Executing bash script with tilde in path - java

I am trying to execute in linux:
command[0] = "~/test/bin/runScript_sh";
Runtime.getRuntime().exec(command);
But get an exception
java.io.IOException: Cannot run program
error=2, No such file or directory
Probably because it can not evaluate tilde.
What can be done?

I would replace it myself.
if(path.s.substring(0,1).contains("~"))
path = path.replaceFirst("~",System.getProperty("user.home"));
Which gets you the string you want.

You can get the user's home directory with System.getProperty:
command[0] = System.getProperty("user.home") + "/test/bin/runScript_sh";

When you run a command at the shell command prompt, things like ~ expansion, quote handling, globbing, $variable expansion, input/output redirection and piping and son on are all handled by the shell ... before it asks the operating system to run the program(s) for you.
When you run a command using Runtime.exec, you have three choices:
write the command without any shell "funky stuff"
replicate what the shell would do in Java; e.g. replace leading tildes with the appropriate stuff1, or
use exec to launch a child shell to run the command; e.g.
Runtime.getRuntime().exec("/bin/sh", "-c", "~/test/bin/runScript_sh");
That is possibly overkill in a simple case like this. But if you are trying to do more complicated things then a child shell can really simplify things.
1 - In fact fully shell compatible handing of tildes is fairly complicated.

Items such as ~ and $HOME are shell expansions
You have to expand these items in your program and then replace them (hint: get them from the os properties, see this page)

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

Executing a native DOS command from a Java application

I am interested in executing the cmd.exe move command from a Java application.
I know how to invoke external processes from within Java. There are 100's of questions on this point in StackOverflow. What I can't figure out, is what the command line should look like.
For example, in a DOS window, this is the command I type:
move dirA dirB
and the directory dirA is moved to directory dirB. Exactly what I want done. For reasons that require far too much context, and will detract from the question, a pure java solution is not an option in the specific context I am concerned with. (Oh the joys of operating in an enormous and complex legacy systems!)
What is the external system command I invoke from java to do that? It seems like it should just be:
"cmd move dirA dirB"
However, that fails for me. I am familiar with this question, but it does not directly answer my question.
No guarantees but I suppose, that you need to prepend the command to run the Windows command shell cmd /c to the command you want to execute. The /c switch terminates the command shell after the desired command completes.
Try:
Runtime.getRuntime().exec("cmd /c move dirA dirB");
EDIT:
As Ian Roberts
have noticed in his comment below, it is also important to take into consideration situation when one or both of directories have spaces in their paths. It is thus much safer to use separate strings to form the finall command, i.e like below:
new ProcessBuilder("cmd", "/c", "move", pathToDirA, pathToDirB).start()

Executing mount command in Java on Android

I'm trying to write Java code that executes some terminal commands. The code should execute this command sudo mount -o loop system.img system. But there are several problems. First, to execute this command I have to be root. I know that I can be by sudo su, but how can I stay as root when I close the terminal window? If I use the command sudo mount -o loop system.img system how can I provide the password in the Java code?
The second issue is: can I execute the command as below?
File f2 = new File("/home/user1/Desktop/aDirectory");
String[] commands = new String[]{"sudo mount", "-o", "loop", "/home/user1/Desktop/aDirectory/system.img"};
Runtime.getRuntime().exec(commands, null, f2);
I think I can't. So how can I do it? Any ideas?
Notes: system.img is a compiled Android os file. and the system is an empty directory. The thing I'm trying to do is mount the system.img file into the system directory.
Programs like sudo read the password directly from the terminal device, not from stdin, so this is unfortunately not a trivial thing to do. I'm not sure if this is realistic for Android or not, but on a general UNIX system the easiest solution is to use expect, which is a library for simulating a terminal and thereby automating these kinds of interactions. It's often used as a standalone program embedded in Tcl, and I've thrown together systems in which Java launched expect to talk to tools like sudo, and it works fine.
expect includes a sort of declarative scripting language that tells it how to run another program and how to react to that program's output.
What you would do is use Runtime.exec() to execute the expect program, supplying a script that just runs "sudo mount", watches for the password prompt, and provides the password. The script would probably just look something like (G4rb4geB4rg3 is the password):
spawn sudo mount -o loop /home/user1/Desktop/aDirectory/system.img
expect "password:"
send "G4rb4geB4rg3\r"
expect eof
The problem was solved, by using shell script.
I wrote a script includes just this line :
echo myPassword | sudo -S mount -o loop system.img system
then I run it in my java code, such :
Runtime.getRuntime().exec("sh 1.sh");
I'm pretty sure 'sudo' and 'mount' would be separate, since it's not a single executable you're invoking. Also, if you start sudo with the -S command line switch it can take the password directly from stdin, so you just need to start the process and pass in whatever the password's configured as to the input stream.

Java option -XX:OnOutOfMemoryError does not work when called through exec in bash script

I have a java application and I want to run a script whenever it experiences and OutOfMemoryException
This works great:
$ java -server -XX:OnOutOfMemoryError="./oom_script %p" TestOOMClass
Unfortunately my application is run by a bash script in production. The script boils down to this:
cmd='java -server -XX:OnOutOfMemoryError="./oom_script %p" TestOOMClass'
##does a lot of checking and cmd building here
exec -a app ${cmd}
When run like this java never respects the double quotes and thinks %p is the class. how do I prevent this? I've tried double escaping but that doesn't work.
Since your program is run as a shell script, I would suggest putting this as the first line in your shell script after the shebang:
set -xv
Then, in the crontab, put 2>&1 at the end of the command line, so STDERR and STDOUT are merged. Crontab usually emails out the STDOUT of a command to root, so you can see what the output is. If not, then apend the following to the end of the command in your crontab:
> /somedir/output.$$ 2>&1
Make sure somedir exists, and after crontab runs your command, you'll see the verbose and debug output. Each line in your shell script will be displayed before it is executed -- both as written and as the shell actually interprets it.
The set -xv becomes very useful in debugging any sell script. There could be all sorts of environmental issues involved between the cronjob and the script running under your login. You might even find a shell issue. For example, crontab usually executes shell scripts in Bourne shell and you probably have Bash or Kornshell as your default shell. Whatever it is, you'll usually find out the issue very quickly when you turn on verbose/debug mode.
You don't even have to do this to the entire script. You can put set -xv anywhere in your script to turn on verbose/debug mode, and set +xv to turn it off.
I could make several pious high minded recommendations (use quotes, don't assume environment things, prefix your command line with "bash -c" to make sure you're using the right shell, etc.), but this would be guessing what could be wrong. In order to really debug this issue, I would need to see the machine, know the OS, see your entire shell script, and understand the entire environment. And, the first thing I would do is add set -xv in your shell script.
Quotes and escaping is an art. I would suggest you add echo ${cmd} before calling exec so you can see what it looks like then.
I would suggest using
cmd='java -server -XX:OnOutOfMemoryError=\\"./oom_script %p\\" TestOOMClass'
instead (untested). You need it to look like \" when being echoed.
an alternative i suggest (to bypass the problem, not solve it indeed) is to rung and bash script and access the $PPID:
PPID The process ID of the shell's parent. This variable is readonly.
then kill the process with that ID (please bare in mind that is an untested suggestion)

Java : how to determine disk space on Windows system prior to 1.6

I want to determine the available disk space on windows. I don't care that my code is not portable. I use this :
String[] command = {"dir",drive};
Process process = Runtime.getRuntime().exec(command);
InputStream result = process.getInputStream();
aiming to parse the result from a "dir C:" type of call, but the String I get from the command line call is as if I called dir with a /W option (not giving any information about file sizes or disk usage / free space). (Although when I launch dir C: directly from the command line, I get the expected result, so there is no dir particular setup on my system.) Trying to pass a token /-W or on any other option seems not to work : I just get the name of the folders/files contained in the drive, but no other information whatsoever.
Someone knows a fix / workaround ?
NOTE:
I can't go along the fsutil route, because fsutil does not work on network drives.
It sounds like your exec() is finding a program called "dir" somewhere in your path because with your String[] command as it is I would otherwise expect you to get an IOException (The system cannot find the file specified). The standard dir command is built into the cmd.exe Command Prompt and is not a standalone program you can execute in its own right.
To run the dir command built into cmd.exe you need to use the /c switch on cmd.exe which executes the specified command and then exits. So if you want to execute:
cmd /c dir
your arguments to pass to exec would be:
String[] command = { "cmd", "/c", "dir", drive };
If you don't care about portability, use the GetDiskFreeSpaceEx method from Win32 API. Wrap it using JNI, and viola!
Your Java code should look like:
public native long getFreeSpace(String driveName);
and the rest can be done through the example here. I think that while JNI has its performance problems, it is less likely to cause the amount of pain you'll endure by using the Process class....
Apache Commons has FileSystemUtils.freeSpaceKb() that will work cross platfrom etc etc

Categories

Resources