I need to execute .bat files in my java application. Suppose I have a text file with this sample content:
{
"id": 12,
"name": "test"
}
And in my .bat file, I have a command for outputing text file content. So this is my .bat file content:
#some other commands
more path\to\file.txt
And finally, this is my java code for executing this .bat file:
Process process = Runtime.getRuntime().exec("path\to\file.bat");
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
List<String> outputs = new ArrayList<>();
while ((line = reader.readLine()) != null) {
outputs.add(line);
}
After executing this code, the outputs list has data something like this:
[...,
"{",
" "id": 12",",
" "name": "test",",
"}"
]
I means, this returns output line by line. But I want to have whole command output as one index of my list. In the other words, I want to have command by command instead of line by line output(every command has just one output).
Is this possible doing something like that?
Edit: I tried using ProcessBuilder also, but result was the same.
You claim
And in my .bat file, I have a command for outputing text file content.
and
I want to have command by command instead of line by line output(every command has just one output).
If I'm understanding this correctly, that means that you run your code only once (one "command") every time that you want to output a file. That is, you're only requiring that the outputs described are joined together in a single line, at which point you can put the lines in a list. This can be achieved like so:
Process process = Runtime.getRuntime().exec("path\to\file.bat");
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
StringBuilder builder = new StringBuilder();
List<String> outputs = new ArrayList<>();
//if desired, append prefix
//builder.append("File: {");
while ((line = reader.readLine()) != null) {
builder.append(line);
//if desired, append delimiter
//builder.append("\n");
}
//if desired, append suffix
//builder.append("}");
String concatenatedString = builder.toString();
Alternatively, in Java 8+, you can do the following (and even specify details of how lines are joined together):
Process process = Runtime.getRuntime().exec("path\to\file.bat");
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String concatenatedString = reader.lines().collect(Collectors.joining(" "));
Naturally, I'm assuming that you're just using the example of reading files as a proxy for another where you must actually read a process' output. If all you require is a file read, a Process is not required to achieve this. Instead, you can get file contents as so:
String concatenatedString = "";
try (Stream<String> stream = Files.lines(Paths.get("path\to\text\file.txt"))) {
concatenatedString = stream.collect(Collectors.joining(" "));
}
//catch...
Now, if you actually want to join all text output together from many processes, but only have it as an aggregate (i.e. you can't join process outputs one by one, then store them), you're going to end up having to do the following:
Join all the strings:
This is easily doable using StringBuffer append or Collectors join as shown above.
Split them apart at the right places:
You will have to identify some marker of the separations between the relevant process outputs (for example, the text of the commands, or maybe the character at the end of the prompt). After identifying the marker(s), you'll have to write a regular expression or parser to separate out the relevant parts of your input, using methods like String substring or StringBuffer substring. If you use regular expressions to match the markers in your input to capturing groups, you can use region, start, and end to greatly simplify the process of splitting up your input.
As #Mofi and #kriegaex stated you should explain the use of batch files. I suppose that you already have them (batch files or some other executables) and you can not get rid of them but instead want to execute them and capture stdout of each execution into a single List or Map item.
Your current implementation appends each line into List:
while ((line = reader.readLine()) != null) {
outputs.add(line);
}
One solution is to use StringBuilder to concatenate stdout lines of each executable. After that each concatenated output is appended into Map. See this sample:
// Create Map for outpus.
Map<String, String> outputs = new HashMap<String, String>();
// Loop through batch files.
for (String bat : new String[] { "file12.bat", "file13.bat" }) {
// Execute batch.
Process process = Runtime.getRuntime().exec(bat);
// Open Reader...
BufferedReader reader =
new BufferedReader(new InputStreamReader(process.getInputStream()));
// ... and read contents into StringBuilder.
StringBuilder contents = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
contents.append(line);
contents.append("\n"); // Append newline because reader.readLine() omits it.
}
// Close Reader.
reader.close();
// Add contents into Map.
outputs.put(bat, contents.toString());
}
After that you can verify Map contents for example like this:
for (String bat : outputs.keySet()) {
System.out.print("### output of ");
System.out.print(bat);
System.out.println(" ###");
System.out.println(outputs.get(bat));
}
It looks as if you do not want to perform a System.out.println() and instead collect all the output of a command and print it in bulk at the end of each command.
Well, then, write your own CommandResultCollector type where you initiate a StringBuffer and concatenate strings with proper line breaks as part of a single command execution and at the end of the command execution, convert it to a String and print the whole thing.
Alternatively, you can create an ArrayList and add all the Strings that are being printed as part of the command and iterate at the end of the execution to print them all at the end of every command execution.
I am sure there are better solutions that use the Heap memory intelligently. You can solve this problem in many ways, choose the simplest and least resource intensive one.
Related
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);
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!
Hy.
I've created a routine that read .tgz files from a directory and unzip each one. I'm using
Process zip01 = Runtime.getRuntime().exec("LINE OF COMMAND");
and
exitVal = zip01.waitFor();
I,m using 7z.exe from its folder to decompress and compress files. The command line is working fine. Now, I what to read the percentage of the decompress and throw it into a textfield or a textarea. The graphics part are ok too, s well all the routine. The only dificult is to get the realtime percentage of the 7z. is there some way to read and show it?
Thanks!
You can get the output of your process like this:
Process zip01 = Runtime.getRuntime().exec("LINE OF COMMAND");
BufferedReader output = new BufferedReader(new InputStreamReader(zip01.getInputStream()));
String line;
while ((line = output.readLine()) != null) {
/* process lines */
}
I'm trying to use the Java Runtime.getRuntime().exec(String) command to run Festival, then use OutputStreamWriter to write some commands to the outpustream of the process.
This works great, and I'm able to do something like this:
Process p = Runtime.getRuntime().exec("festival");
Writer w = new OutputStreamWriter(p.getOutputStream());
w.append("(SayText \"Hello World\")");
w.flush();
Obviously the way I can tell this works is that it speaks the text through the speakers.
What I am having a real hard time doing is getting the text output from what I would see in the terminal. I'm trying to run some other commands (such as (voice.list)) which output text, presumably to stdout.
For example, I've tried using a BufferedReader in the following way:
BufferedReader reader = new BufferedReader (new InputStreamReader(p.getInputStream()));
w.append("(voice.list)");
w.flush();
String output = "";
String line = reader.readLine();
System.out.println(line);
while ((line = reader.readLine()) != null)
{
System.out.println("Reading: " + line);
output += line;
}
(The System.out.println's is just for debugging, I would do the entire thing in a cleaner way if I was able to get it to work.)
No matter what code I try, I'm never able to get any output from Festival. I can get output from other commands. E.G. I have tried this code as well http://en.allexperts.com/q/Java-1046/2008/2/Runtime-getRuntime-exec-cmd.htm and it works with many other commands (like ls) but not Festival.
Does anything have any idea how I would be able to get this to work?
Thanks.
Festival may output it's text on stderr instead of stdout. Try replacing
p.getInputStream()
with
p.getErrorStream()
In one commend , I'm trying to send data to System.out like this:
And in another command I'm trying to get this data from System.in.
It's strange because, it works once of many tries. I can try to run it 10 times and it's still inReader.ready() == false, and when I run it for example 11th time , it works.
Why ? How can I fix this? How to make it work everytime ?
Thanks, in advance !
You can't read your InputStream that way, since the data may not have been arrived at the second process yet. You can either read character by character, with something like:
InputStreamReader inReader = new InputStreamReader(System.in);
int data = inReader.read();
while (data != -1){
...
data = inReader.read();
}
or simple read the input line by line, using:
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
while ((String line = br.readLine()) != null) {
...
}
If your objective is to execute a shell command, don't use System.out but Runtime.getRuntime().exec(cmd) instead. Check out this question for more details.