Passing parameter with space to Shell Script using Java - java

I've had trouble passing parameters with spaces to a shell script with the following JAVA code:
List<String> parameterList = new ArrayList<String>();
parameterList.add(executable);
parameterList.add(inputFile);
parameterList.add(outputPath);
ProcessBuilder pb = new ProcessBuilder(parameterList);
pb.redirectErrorStream();
Process p = pb.start();
BufferedReader reader = new BufferedReader(newInputStreamReader(p.getInputStream()));
#SuppressWarnings("unused")
String line = null;
while ((line = reader.readLine()) != null) {
}
The problem comes when passing my parameter to the 2nd add to the list in the input file variable.
If I use my bat file, I can get the string with spaces, say "my data.xml" using the variable %~1. However, in LINUX, I cannot use %~1 instead there is "$1" which does not accept strings with spaces. Is there a similar approach I can use that will allow me to pass parameters with spaces to a shell script?
EDIT:
In the console the command should go like this:
run.sh "/scratch/input/my data.xml" /scratch/output/
Is there a way to have the above command be called from JAVA verbatim?
Thanks!

Related

Windows - Using set and echo in Java program

I want to set and echo a Windows variable in Java:
public static void main(String[] args) throws IOException
{
Runtime rt = Runtime.getRuntime();
String[] cmd = { "cmd.exe", "/c", "set HOSTNAME=%COMPUTERNAME% "
+ "&& echo %HOSTNAME%" };
Process proc = rt.exec(cmd);
BufferedReader stdInput = new BufferedReader(
new InputStreamReader(proc.getInputStream()));
BufferedReader stdError = new BufferedReader(
new InputStreamReader(proc.getErrorStream()));
System.out.println("Output:\n");
String s = null;
while ((s = stdInput.readLine()) != null)
{
System.out.println(s);
}
System.out.println("Error (if any):\n");
while ((s = stdError.readLine()) != null)
{
System.out.println(s);
}
}
I expect the program will print out my computer host name or I will use this value for another purpose. But the output is just like this:
Output:
%HOSTNAME%
Error (if any):
How could I get the value that I have set in the command set HOSTNAME=%COMPUTERNAME%
It's irrelevant to Java because it's how cmd parses the command. The whole command will be parsed at once for variable expansion. At the time the command is parsed the variable is not yet available, so it'll be replaced with nothing in a batch file or leave as-is in command line
You need to use delayed expansion and print the variable with !!
cmd.exe /V:ON /c set HOSTNAME=%COMPUTERNAME% && echo !HOSTNAME!
The /V:ON is for enabling delayed expansion
CMD [/A | /U] [/Q] [/D] [/E:ON | /E:OFF] [/F:ON | /F:OFF] [/V:ON | /V:OFF]
[[/S] [/C | /K] string]
...
/V:ON Enable delayed environment variable expansion using ! as the
delimiter. For example, /V:ON would allow !var! to expand the
variable var at execution time. The var syntax expands variables
at input time, which is quite a different thing when inside of a FOR
loop.
In a batch file it can be enabled by setlocal EnableDelayedExpansion
However for that purpose just cmd.exe /V:ON /c echo %COMPUTERNAME% is enough. Yet it's still not the efficient way. There are better ways to get hostname in Java
Map<String, String> env = System.getenv();
if (env.containsKey("COMPUTERNAME"))
return env.get("COMPUTERNAME");
or
InetAddress.getLocalHost().getHostName()
Your syntax for running two commands at once is wrong. Try using a single & in the command line instead of &&.
The real problem, I think, is that cmd.exe does all variable substitution before executing the command line (including parsing the &&). When it finds the syntax %HOSTNAME% for a variable that doesn't exist (yet), it leaves the text as is: %HOSTNAME%. So try issuing two commands to the same process, followed by an exit command.
Another approach is to change the command to:
set HOSTNAME=%COMPUTERNAME% & SET HOSTNAME
Then you will get back the string "HOSTNAME=my_computer_name", from which you can strip out the leading "HOSTNAME=" prefix.

Java: Convert command array to string that can execute in bash

I have a command in array format that I can pass to execve(). For example, the command is:
echo "It's Nice"
and the array I have is ["echo","It's Nice"]. I'm trying to convert this array into a string that I can write in bash and execute properly. I obviously cannot join on this array with space delimiter because I will get: echo It's Nice which cannot be run since it has an unterminated single quote.
Is there a BKM to convert this to a runnable string? maybe a library that does that already in Java? It can get tricky when the command has many special characters that should be escaped\quoted in order to run properly.
EDIT:
I would like to make my question clearer. The user gives me his command as a string array, I execute it and everything works fine. Now I need to report to the user what I have ran. I do not want to show the command as an array, instead I would like to show it as a string that the user can simply copy and paste to his bash shell and execute it if he wants to. So my input is [echo, It's Nice] and my output should be echo "It's Nice". It seems like a simple function to write, but i'm not sure i'm thinking of all the end-cases here (like if the string has a quote or some other special character the shell manipulates). I was wondering maybe there's some code that already does that and covers the end cases i'm yet to think about.
You don't need to convert array to string, you can directly execute a command using ProcessBuilder:
String runShell(final String[] commandArgs) {
try {
final ProcessBuilder processBuilder = new ProcessBuilder(commandArgs);
processBuilder.redirectErrorStream(true); // merge stderr with stdout
Process process = processBuilder.start();
ret = process.waitFor();
BufferedReader br = new BufferedReader(new InputStreamReader(
process.getInputStream() ));
br.lines().forEach(System.out::println); // print stdout + stderr
process.destroy();
br.close();
}
catch (IOException|InterruptedException e) {
e.printStackTrace();
}
return commandArgs[0] + Arrays.asList(commandArgs).stream()
.skip(1)
.collect(Collectors.joining("\" \"", " \"", "\""));
}
and call it as:
runShell(new String[] {"pwd"}); // print current working directory
runShell(new String[] {"ls", "-l"}); // list all the files and directories
runShell(new String[] {"echo", "It's Nice"}); // echo something
That's easy to do in Java 8:
String joined = String.join(" ", iteratable);

getting terminal output into java

I use the following code to get output of terminal command into my java program.
check = CharStreams.toString(new InputStreamReader(java.lang.Runtime.getRuntime().exec(command).getInputStream()));
when command = "ifconfig" this works fine. but when command = "ldapwhoami -D \"cn=admin,dc=example,dc=com\" -w password", the variable check is empty. When i run the same command in the terminal myself i get the desired output. What am i doing wrong here? please help.
You are not escaping special characters in your command string.
If the command needs double quotes, it should be:
command = "ldapwhoami -D \"cn=admin,dc=example,dc=com\" -w password"
Note the \" to insert double quotes inside string.
Also try in this way:
Process p = Runtime.getRuntime().exec(command);
BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line = null;
while ((line = in.readLine()) != null) {
System.out.println(line);
}
Other option is to use java.util.Scanner.

how to execute batch command (imagemagick) with java

I used the terminal command to convert all the images in the folder into RGB images using imagemagick tool
"C:\Documents and Settings\admin\My
Documents\NetBeansProjects\Archiveindexer\resources\T0003SathyabamaT\Active\CBE_2014_03_02_FE_04_MN_IMAGES_CONVERTED"
is my image folder
terminal command:
myimagefolder> mogrify -colorspace RGB *.jpg
This works fine. But when run this using java it is not working
File destpathfinalconv = new File("C:/Documents and Settings/admin/My Documents/NetBeansProjects/Archiveindexer/T0003SathyabamaT/Active/CBE_2014_03_02_FE_04_MN_IMAGES_CONVERTED");
ProcessBuilder pb = new ProcessBuilder("mogrify", "-colorspace RGB", destpathfinalconv.toString(),
"*.jpg");
pb.redirectErrorStream(true);
Process p = pb.start();
BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line = null;
while ((line = br.readLine()) != null) {
System.err.println(line);
}
System.err.println("Error "+p.waitFor());
System is throwing error "mogrify.exe: unrecognized option
`-colorspace RGB' # error/mogrify.c/MogrifyImageCommand/4254. Error 1"
Any idea please suggest.
You are specifying '-colorspace RGB' as a single argument, but it should be two arguments. And you should combine the path and file and search pattern into a single argument. The constructor of ProcesBuilder should be called like this:
ProcessBuilder pb = new ProcessBuilder("mogrify", "-colorspace", "RGB",
destpathfinalconv.toString() + "\\" + "*.jpg");
Try this:
ProcessBuilder pb = new ProcessBuilder(
"mogrify",
"-colorspace",
"RGB",
destpathfinalconv.toString(),
"*.jpg");
Explanation: Each String argument in the ProcessBuilder ends up as a "word" (according to shell parlance) or a separate parameter in the resulting execve call.
Combining "-colorspace RGB" results in a single parameter to mogrify, which is the (unknown) option "-colorspace\ RGB".

ProcessBuilder executes one command from the String list but doesn't executes the next one.

I am a newbie in programming. I created a String list for ProcessBuilder. ProcessBuilder is executing one command from the String list (and successfully writes a file I asked to write) but doesn't execute the next String command. Here is the code:
List<String> vsArrays = new ArrayList<String>();
vsArrays.add("/bin/sh");
vsArrays.add("-c");
vsArrays.add("echo '123' > ~/1.rad");
vsArrays.add("echo '123' > ~/2.rad");
vsArrays.add("echo '123' > ~/3.rad");
for (String s : vsArrays){
system.out.println(s);
}
ProcessBuilder proc = new ProcessBuilder(vsArrays);
Process start = proc.start();
start.waitFor();
The first file (named 1.rad) is created in the home (~) directory but the following files are not. Although I managed to execute the next commands (and write the other files) by using redirectInput from a file, but I dont want to create a separate file for redirectInput. Can you please answer why the next commands (from the string list) are not getting executed? Thanks!
ProcessBuilder was never meant to execute multiple commands. The entries of the List are treated as arguments and it is the invoked program sh which executes the one argument due to the -c option. So it’s the sh command and it’s -c option which “decide” to interpret one argument, and only one, as a command to start. But the bash will run multiple commands being provided as a single argument separated by ;.
List<String> vsArrays = new ArrayList<String>();
vsArrays.add("/bin/sh");
vsArrays.add("-c");
vsArrays.add("echo '123' > ~/1.rad; echo '123' > ~/2.rad; echo '123' > ~/3.rad");
ProcessBuilder proc = new ProcessBuilder(vsArrays);
Process start = proc.start();
start.waitFor();
I haven't used process builder for a while, but I believe you are passing the 4 last strings as arguments to the first string (the executable). In which case, I'm not sure the command you have constructed i valid syntax. Try something like (haven't tested the code myself but should give you some ideas):
private void myMainFunction() {
doWrite("1.rad");
doWrite("2.rad");
doWrite("3.rad");
}
private void doWrite(String filename) {
List<String> vsArrays = new ArrayList<String>();
vsArrays.add("/bin/sh");
vsArrays.add("-c");
vsArrays.add("echo '123' > ~/" + filename);
for (String s : vsArrays){
system.out.println(s);
}
ProcessBuilder proc = new ProcessBuilder(vsArrays);
Process start = proc.start();
start.waitFor();
}

Categories