Parse Output of Child Process - java

In Java, I am launching a child process like so:
String[] command = {"java", "-jar", jarPath};
Process childProcess = new ProcessBuilder().inheritIO().command(command).start();
I expect the child process to print Success! to standard output at some point (if things are successful). How do I periodically (e.g., every 5 seconds) check if there's a Success! line in the output that is created by the child process?

If you want to perform IO with the subprocess, don’t use inheritIO. Instead, capture the process’ output stream via getInputStream() and read it via a BufferedReader:
final Process childProcess = new ProcessBuilder().command(command).start();
try (final BufferedReader br = new BufferedReader(new InputStreamReader(childProcess.getInputStream()))) {
String line;
while ((line = br.readLine()) != null) {
if (line.equals("Success!")) {
System.out.println("Successfully completed");
// Maybe, depending on whether you need the remaining output:
break;
} else {
// Handle output from process; e.g.:
System.out.println("No success yet.");
}
}
}
// At this point, the child process has not yet necessarily completed!
childProcess.waitFor();
Importantly, you don’t need to check for output periodically: the loop performs synchronous IO, that is, it will automatically wait until there’s output available from the child process. All you need to do is check whether the output is what you expect.

So... this is rather a loaded question, although that might not be obvious from just asking it.
I was not able to do this using the ProcessBuilder class
(maybe someone with more experience can answer using that route).
Here is what you need to do: use the class Runtime.getRuntime().exec(command) to run your command. This gives you easy access to the standard input and standard error streams.
Below is a general complete class you can use.
Usage:
final String jarPath = "jarPath.jar";
final String[] command = { "java.exe", "-jar", jarPath };
final CrossPlatformCommand childProcess = new CrossPlatformCommand();
final int exitCode = childProcess.execute(command[0] + " " + command[1] + " " + command[2]);
System.out.println("Parse child process output: " + childProcess.getInput());
Output:
Parse child process output: Success!
There are a few caveats with this: the dollar sign needs to be escaped in Linux. There may be other characters that need to be escaped (this has not been investigated fully, and you may need to modify this class if you are using other characters that need to be escaped.)
public class CrossPlatformCommand
{
private static final String SHELL_LINUX = "/bin/bash";
private static final int BUFFER_SIZE = 4096;
private static final String OS_NAME = System.getProperty("os.name").toLowerCase(Locale.getDefault());
private final StringBuffer error = new StringBuffer();
private final StringBuffer input = new StringBuffer();
public int execute(final String command) throws Exception
{
final int exitCode;
if (OS_NAME.startsWith("windows"))
{
exitCode = executeWindows(command);
}
else
{
exitCode = executeLinux(command); // OS_NAME.startsWith("linux")
}
return exitCode;
}
public String getError()
{
return this.error.toString();
}
public String getInput()
{
return this.input.toString();
}
private int executeLinux(final String command) throws Exception
{
final Process proc = Runtime.getRuntime().exec(SHELL_LINUX);
processLinuxCommand(proc, command);
return processStreams(proc);
}
private int executeWindows(final String command) throws Exception
{
final Process proc = Runtime.getRuntime().exec(command);
return processStreams(proc);
}
private static void processLinuxCommand(final Process proc, final String command) throws Exception
{
try (BufferedWriter out = new BufferedWriter(new OutputStreamWriter(proc.getOutputStream())))
{
// Dollar signs are special in Linux.
out.write(command.replace("$", "\\$"));
out.newLine();
out.flush();
}
}
private int processStreams(final Process proc) throws Exception
{
fillBuffer(proc.getInputStream(), this.input);
fillBuffer(proc.getErrorStream(), this.error);
return proc.waitFor();
}
private static void fillBuffer(final InputStream in, final StringBuffer sb) throws IOException
{
sb.setLength(0);
final BufferedReader reader = new BufferedReader(new InputStreamReader(in));
final char[] buffer = new char[BUFFER_SIZE];
final int length = reader.read(buffer);
for (int i = 0; i < length; i++)
{
sb.append(buffer[i]);
}
}
}

Related

how to extract specific lines from msinfo32 file using java code

i am writing a code to extract system info details i.e ram, processor speed and put them on a text file.
public void getSpecs(){
//run a cmd command to convert msinfo32 to .txt file
String[] command = {
"cmd",
};
Process p;
try{
p= Runtime.getRuntime().exec(command);
new Thread(new Sec(p.getErrorStream(), System.err)).start();
new Thread(new Sec(p.getInputStream(), System.out)).start();
PrintWriter pw= new PrintWriter(p.getOutputStream());
pw.println("msinfo32 /report .\\specs.txt");
pw.close();
p.waitFor();
}catch(Exception e){
e.printStackTrace();
}
}
}
This process is taking long and its converting the whole file.
msinfo32 exports the computer info into a file. It is expected to take some time as it retrieves a huge export for each computer/windows component.
I have done something similar using powershell
public static void main(String[] args) throws IOException {
//Set the commands
String cmd = "powershell.exe get-WmiObject ";
String[] win32CmdA = {"win32_processor", "win32_computerSystem", "win32_logicaldisk"};
for (String win32Cmd : win32CmdA) {
String info = runCmd(cmd + win32Cmd,
"MaxClockSpeed",
"TotalPhysicalMemory",
"DeviceID",
"FreeSpace");//Add as many atributes you want to return from powershell output
System.out.println(info); // You can use a file writer here
}
// //You can handle ErrorStream here
// String line;
// BufferedReader stderr = new BufferedReader(new InputStreamReader(
// powerShellProcess.getErrorStream()));
// while ((line = stderr.readLine()) != null) {
// System.out.println(line);
// }
}
private static String runCmd(String cmd, String... attrs) throws IOException {
Process powerShellProcess = Runtime.getRuntime().exec(cmd);
powerShellProcess.getOutputStream().close();
String line;
String result="";
BufferedReader stdout = new BufferedReader(new InputStreamReader(powerShellProcess.getInputStream()));
while ((line = stdout.readLine()) != null) {
if (line != null && line.contains(":")) {
String nameValue[] = line.split(":");
if (Arrays.asList(attrs).contains(nameValue[0].trim())) {
result+=nameValue[0] + " - " + nameValue[1] + "\n";
}
}
}
stdout.close();
return result;
}
The above code invokes the Windows Management Instrumentation (WMI) classes in powershell for specific components (processor, computerSystem and logicaldisk).
Then you define which values should be taken from the powershell output, like MaxClockSpeed, TotalPhysicalMemory, etc.
If you change the System.out.println(info); with a file writer you will have this info in a file.
Sample output (took ~3 seconds to run)
DeviceID - CPU0
MaxClockSpeed - 3401
TotalPhysicalMemory - 17053949952
DeviceID - C
FreeSpace - 56341774336
DeviceID - D
FreeSpace -
DeviceID - F
FreeSpace - 373687742464

How can I Write contents in the nextline within a File? and Finding a String inside a File and Replacing it at the same exact line

I am using a SpringBoot application and have introduced a "Like" button for Dog Pictures displayed and upon each Like it will hit the below #RequestMapping(value="likedDog") i am Concatenating the necessary information with a delimiter.
Method Inside the Controller shown below:
#RequestMapping(value = "likedDog")
public String likedDogList(#RequestParam("breedType") String breedType, #RequestParam("id") String id, Model model){
Long likes=0L;
String delimiter="_!_";
Dogs dogObject= dogsService.returnDogById(Long.valueOf(id));
if(dogObject.getLikes()==null){
likes=0L;
}
else{
likes=dogObject.getLikes();
}
likes=likes+1;//1,2
dogObject.setLikes(likes);//set 1,2
dogObject=dogsService.saveDog(dogObject);
model.addAttribute("dogObj",dogObject);
model.addAttribute("dogBreedType",breedType);
String content=id+delimiter+breedType+delimiter+dogObject.getImgURL()+delimiter+dogObject.getLikes();
String contentSub=id+delimiter+breedType+delimiter+dogObject.getImgURL();
try{
File file = new File("src/main/resources/data/DogLikes.txt");
if (!file.exists()) {
file.createNewFile();
}
FileWriter fw = new FileWriter(file.getAbsoluteFile());
BufferedWriter bw = new BufferedWriter(fw);
bw.write(content);
bw.newLine();
bw.flush(); // ORIGINAL
bw.close();
}
catch(Exception e){
System.out.println("Exception occured in likedDogList()");
e.printStackTrace();
}
return "liked";
}
I want to Write the Concatenated String "content" on a newline in the respective file if likes belong to different dogs, the code is shown below, even though I introduce bw.newLine i cant write it in newline instead it simply replaces the old existing content in the first line of the file.
Contents in the File DogLikes.txt shown below:
17_!_Pug_!_http://i.imgur.com/E5vBM5Z.png_!_1 //but on every click the contents are being replaced in this first line only
Expected Output, the contents to be written into DogLikes.txt on every Like is shown below:
17_!_Pug_!_http://i.imgur.com/E5vBM5Z.png_!_1
18_!_Pug_!_http://i.imgur.com/E5vBM5Z.png_!_1 //should be written like this in newline on like of dogId 18
19_!_Pug_!_http://i.imgur.com/E5vBM5Z.png_!_1 //should be written like this in newline on like of dogId 19
SECOND PROBLEM:
Also if i click "Like" on DogId 18 it should go and replace the second line as follows,
17_!_Pug_!_http://i.imgur.com/E5vBM5Z.png_!_1
18_!_Pug_!_http://i.imgur.com/E5vBM5Z.png_!_2 //replacement of String in this exact location is expected
19_!_Pug_!_http://i.imgur.com/E5vBM5Z.png_!_1
Your help on this is much appreciable, Thanks in advance.
hey #daniel the backup method worked for writing down the contents into the file line by line while i am using a different approach for restoring. However I came up with a new idea to incorporate both likes and dislikes into the same line as shown below:
Expected output on clicking "Like" and "Dislike"
1_!_Labrador_!_http://i.imgur.com/eE29vX4.png_!_like_!_11_!_dislike_!_5
2_!_Labrador_!_http://i.imgur.com/xX2AeDR.png_!_like_!_3_!_dislike_!_5
3_!_Labrador_!_http://i.imgur.com/hBFRUuW.png_!_like_!_1_!_dislike_!_5
16_!_Pug_!_http://i.imgur.com/E5vBM5Z.png_!_like_!_119_!_dislike_!_5
I kinda need your help in backup() method as i tried different combinations to update likes and dislikes in the same line, as your approach dynamically updates only likes. Also I'm lookinf forward to sort the dogs images according to the highest likes to lowest.
Thank you for your help in advance.
As you described in your question your requirements were to use an in-memory storage and a backup mechanism to a particular file in the file system. I tried a short prototype that makes use of spring mvc and data rest. You can find here: https://github.com/dbubenheim/stackoverflow-40820684
The most interesting part is the backup and restore process of the data for which I have the following two service classes:
#Service
public class DogBackupServiceImpl implements DogBackupService {
#Value("${dog.backup.file}")
private String dogLikesFile;
private static final String DELIMITER = "_!_";
#Override
public void backup(final Dog dog) throws IOException {
final Path dogLikesPath = Paths.get(this.dogLikesFile);
if (!Files.exists(dogLikesPath)) {
Files.createDirectories(dogLikesPath.getParent());
Files.createFile(dogLikesPath);
}
final File dogLikesFile = Paths.get(this.dogLikesFile).toFile();
final StringBuilder stringBuilder = new StringBuilder();
String currentLine;
String oldLine = null;
try (final Scanner scanner = new Scanner(dogLikesFile)) {
while (scanner.hasNextLine()) {
currentLine = scanner.nextLine().trim();
stringBuilder.append(currentLine);
if (currentLine != null && !currentLine.isEmpty()) {
stringBuilder.append("\n");
}
if (currentLine != null && currentLine.startsWith(Long.toString(dog.getId()))) {
oldLine = currentLine;
}
}
}
String content = stringBuilder.toString();
final boolean firstLike = (oldLine == null || oldLine.isEmpty());
final String updateLine = dog.getId() + DELIMITER + dog.getBreedType() + DELIMITER + dog.getImgUrl() + DELIMITER + dog.getLikes();
if (!firstLike) {
content = content.replace(oldLine, updateLine);
}
try (final BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(dogLikesFile))) {
bufferedWriter.write(content);
if (firstLike) {
bufferedWriter.write(updateLine);
}
bufferedWriter.close();
}
}
}
and
#Service
public class DogRestoreServiceImpl extends ContextLoaderListener implements DogRestoreService {
#Value("${dog.backup.file}")
private String dogLikesFile;
private static final int FIELD_ID = 0;
private static final int FIELD_BREED_TYPE = 1;
private static final int FIELD_IMG_URL = 2;
private static final int FIELD_LIKES = 3;
private static final String DELIMITER = "_!_";
private final DogRepository dogRepository;
public DogRestoreServiceImpl(final DogRepository dogRepository) {
this.dogRepository = requireNonNull(dogRepository, "dogRepository must not be null!");
}
#Override
public void restore() throws IOException {
final Path path = Paths.get(this.dogLikesFile);
if (!Files.exists(path)) {
System.out.println("nothing to restore");
return;
}
final File dogLikesFile = path.toFile();
final Set<Dog> dogs = new HashSet<>();
String line;
try (final BufferedReader reader = new BufferedReader(new FileReader(dogLikesFile))) {
while ((line = reader.readLine()) != null) {
final String[] fields = line.split(DELIMITER);
final Dog dog = new Dog.DogBuilder(Long.parseLong(fields[FIELD_ID]))
.breedType(fields[FIELD_BREED_TYPE])
.imgUrl(fields[FIELD_IMG_URL])
.likes(Long.parseLong(fields[FIELD_LIKES]))
.build();
dogs.add(dog);
}
this.dogRepository.save(dogs);
}
}
#Override
public void contextInitialized(final ServletContextEvent event) {
try {
System.out.println("context initialized. restoring data...");
this.restore();
System.out.println("data restoring completed!");
} catch (final IOException ioException) {
ioException.printStackTrace();
}
}
}
The idea is: The backup process saves every time the spring data repository saves an enitity to keep the backup file up to date.
The restore process is basically triggered when the spring context is successfully initialized (when the application starts). So you should be able to have all the data both in memory and in the backup file.
You can interact with the service for example via cURL with the following basic actions:
# retrieve all dogs
curl -X GET "http://localhost:8080/dogs"
# post dog
curl -X POST -H "Content-Type: application/json" -d '{
"breedType": "Husky",
"imgUrl": "http://i.imgur.com/abc123.png",
"likes": 0
}' "http://localhost:8080/dogs"
# like dog
curl -X POST -H "Content-Type: application/json" -d '' http://localhost:8080/dogs/likeDog/<dogs-id>"
Hope that helps! If there are any further questions let me know.

How to input in a program running in cmd using java

I'm creating a program that will accept source codes, compile it, and input test cases to see whether the program was correct. A source code checker, if you may. What I used for compiling programs is through cmd. My problem now is how to enter the test cases once the programs are running in cmd.
This is actually a program for our school. So the professor will give a problem (ex. Input an integer, say if it's even or odd) then this program will check the students' source codes by testing the test cases provided by the professor (ex. input: 1 output: odd, input 2: output: even).
Here's my sample code (c# compiler)
case ".cs":
CsCompiler();
Run(path + "\\program");
break;
My functions:
public static void CsCompiler() throws IOException, InterruptedException {
Process(path + "\\", " c:\\Windows\\Microsoft.NET\\Framework\\v3.5\\csc /out:program.exe *.cs");
}
public static void Process(String command, String exe) throws IOException, InterruptedException {
final Process p;
if (command != null) {
p = Runtime.getRuntime().exec(exe, null, new File(command));
} else {
p = Runtime.getRuntime().exec(exe);
}
new Thread(new Runnable() {
public void run() {
BufferedReader input = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line = null;
try {
while ((line = input.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
p.waitFor();
}
public static void Run(String command) throws IOException, InterruptedException {
String[] argss = {"cmd", "/c", "start", command};
ProcessBuilder pb;
pb = new ProcessBuilder(argss);
pb.start();
}
If I got you right you want to start a program and fetch its output while injecting input from your java method. Actually thats very simple since you already did the ouput fetching in your compile method.
The Process Class also has a getOutputStream method that you can use for injecting input to your process.
I will show how to do this with an example.
Consider this simple C programm as a students source code that takes a number as input and checks if it is even or odd.
#include <stdio.h>
int main(){
int x;
printf("Enter an Integer Number:\n");
if (( scanf("%d", &x)) == 0){
printf("Error: not an Integer\n");
return 1;
}
if(x % 2 == 0) printf("even\n");
else printf("odd\n");
return 0;
}
Now implement your Run method like this to run the application, inject input, read output and check the return value.
public static void Run(String command, String input) throws IOException, InterruptedException {
// create process
String[] argss = {"cmd", "/c", command};
ProcessBuilder pb = new ProcessBuilder(argss);
Process process = pb.start();
// create write reader
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(process.getOutputStream()));
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
// write input
writer.write(input + "\n");
writer.flush();
// read output
String line = "";
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
// wait for process to finish
int returnValue = process.waitFor();
// close writer reader
reader.close();
writer.close();
System.out.println("Exit with value " + returnValue);
}
And thats it. If you invoke this method like this
Run("NumberChecker.exe", "1");
Run("NumberChecker.exe", "2");
Run("NumberChecker.exe", "a");
You will get the following output
Enter an Integer Number:
odd
Exit with value 0
Enter an Integer Number:
even
Exit with value 0
Enter an Integer Number:
Error: not an Integer
Exit with value 1

put the output of the shell script into a variable in a Java program

Is there a way to get an output from a shell script program into a variable in Java program(not into the output file). The output of my shell script is the database query execution time and I need to assign that time value to a Java variable. (I am calling that shell script from Java program). And then I will need to do some other calculations on those values in Java.
Update to old question
Since Java 7 there is a new class which can easily deal with OS procecces: ProcessBuilder
.Let's assume we need to store the output of ip_conf.bat into a java String. Contents of c:\tmp\ip_conf.bat
#echo off
REM will go to standard output
ipconfig
REM will go to stadnard error
hey there!
You can read the input streams connected to the standard and error outputs of the subprocess:
ProcessBuilder pb = new ProcessBuilder("C:\\tmp\\ip_conf.bat");
Process p = pb.start();
String pOut = "";
try (InputStream stdOut = p.getInputStream();
BufferedInputStream bufferedStdOut = new BufferedInputStream(stdOut);
ByteArrayOutputStream result = new ByteArrayOutputStream()) {
int bytes = 0;
while ((bytes = bufferedStdOut.read()) != -1) {
result.write(bytes);
}
pOut = result.toString(Charset.defaultCharset().toString());
}
System.out.println(pOut);
InputStream stdErr = p.getErrorStream();
// same with the error stream ...
int exit = p.waitFor();
System.out.println("Subprocess exited with " + exit);
Below is the program that will help you store the full output of any script or any command into String object.
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class ExecuteShellComand {
public static void main(String[] args) {
ExecuteShellComand obj = new ExecuteShellComand();
String output = obj.executeCommand("sh /opt/yourScriptLocation/Script.sh");
System.out.println(output);
}
private String executeCommand(String command) {
StringBuffer output = new StringBuffer();
Process p;
try {
p = Runtime.getRuntime().exec(command);
p.waitFor();
BufferedReader reader =
new BufferedReader(new InputStreamReader(p.getInputStream()));
String line = "";
while ((line = reader.readLine())!= null) {
output.append(line + "\n");
}
} catch (Exception e) {
e.printStackTrace();
}
return output.toString();
}
}
I just google it and there is a nice tutorial, full of examples here : http://www.mkyong.com/java/how-to-execute-shell-command-from-java/
I know people prefere copy/paste but let's respect other people's work and go on their website :p

how to pass parameters to a process during runtime

I have C code that takes input from the console as,
main() {
int value;
printf("Enter a value");
scanf("%d", &value);
printf("the value is, %d", value);
return;
}
now I compile and create an .exe file. (say test.exe)
After that I want to call that executable file from a java code.
so, I used
public class JavaTest {
static String s=null;
public static void main(String[] args) throws java.io.IOException, java.lang.InterruptedException {
java.lang.Runtime rt = java.lang.Runtime.getRuntime();
System.out.println("start");
java.lang.Process p = rt.exec("test.exe");
BufferedReader stdInput = new BufferedReader(new
InputStreamReader(p.getInputStream()));
while ((s = stdInput.readLine()) != null) {
System.out.println(s);
}
p.waitFor();
System.out.println("done.");
}
}
Obviously, that doesn't work. My question is, how do I pass the integer to the process during runtime? I don't want to pass it as command line parameter.
use the outputStream before you use the inputStream of the Process. Write the integer to the output.Stream

Categories

Resources