1

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.

hamed
  • 7,939
  • 15
  • 60
  • 114
  • You want to access the **key** (in this case, a command) and in return, get a **value**, right? – mperic Aug 08 '19 at 18:59
  • Yes, something like this. If I can get a map with command and result it will very good. but, without key is also ok, just having a full(not line by line) result is also good for me. – hamed Aug 08 '19 at 19:04
  • 1
    Don't use `more path\to\file.txt` in batch file, use instead `type "path\to\file.txt"` or read the text file with Java code. If you want to know the differences between the commands `type` and `more`, open a [command prompt](https://www.howtogeek.com/235101/), run `type /?`, read the output help (very short) and `more /?` and read again output short help. – Mofi Aug 11 '19 at 19:09
  • @Mofi Thank you for your response. But difference between `type` and `more` is not really my problem!! – hamed Aug 11 '19 at 19:21
  • I am quite sure that it is not possible from within Java program to control how `cmd.exe` processes the command lines in batch file and the outputs of the commands to handle `stdout` captured by the Java program. I think, the only solution is not using a batch file at all and instead do everything in Java which is done with the batch file. I would really be wondered if this is not possible. If one script interpreter like `cmd.exe` can execute one command/executable after the other to get data, another interpreter like `java.exe` can do the same, for example with using with multiple processes. – Mofi Aug 11 '19 at 19:33
  • 5
    I think, you ask for help on an [XY problem](http://xyproblem.info/). So I recommend to go back a step and think about how to solve the task without using a batch file and capture its output. Well, you could insert in batch file between each command writing to `stdout` a line like `echo NewCmd` and in Java program concatenate everything between two `"NewCmd"` strings to one string with `\n` or `\r\n` before adding it to string array `outputs` and of course discard the strings `"NewCmd"`. But I am 100% sure, there are better solutions for the entire task. – Mofi Aug 11 '19 at 19:42
  • 2
    I agree with @Mofi, this is definitely an instance of the [XY problem](https://meta.stackexchange.com/a/66378/309898). Please explain the situation and the problem you want to solve, not how you think it should be solved. For example, what existed first, the batch files or the Java program? What do the "other commands" do in the batch file? Why do you need to split the output from one batch file into chunks? What benefit would you have from it? – kriegaex Aug 12 '19 at 01:53
  • This question might be a copy of this one. https://stackoverflow.com/questions/17061268/get-output-from-bat-file-using-java – SuperRyn Aug 18 '19 at 13:16

3 Answers3

4

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.
Avi
  • 2,611
  • 1
  • 14
  • 26
0

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));
}
haba713
  • 2,465
  • 1
  • 24
  • 45
0

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.

Rao Pathangi
  • 504
  • 3
  • 11