I am trying to use the cat command from within a java program to merge two files into one. The line of code which contains the cat command takes two files file1 and file2 and writes to a third file called combinedfile. However, what I observe is the instead of creating this file (combinedfile) and writing to it, my program merely displays the output on the terminal.
How can I make sure that indeed the two files are copied to a third file.
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class ExecuteShellCommand
{
public static void main(String[] args)
{
ExecuteShellCommand obj = new ExecuteShellCommand();
String command = "cat file1 file2 > combinedfile";
String output = obj.executeCommand(command);
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();
}
}
EDIT:
I tried out with the ProcessBuilder as suggested, but I get this error.
Code
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.*;
import java.util.*;
public class ExecuteShellCommand
{
public static void main(String[] args)
{
try
{
ProcessBuilder builder = new ProcessBuilder("cat", "/home/PepperBoy/Desktop/file1.txt","/home/PepperBoy/Desktop/file2.txt");
File combinedFile = new File("/home/PepperBoy/Desktop/file3.txt");
builder.redirectOutput(combinedFile);
builder.redirectError(combinedFile);
Process p = builder.start();
}
catch(IOException e)
{
e.printStackTrace();
}
}
}
Error
ExecuteShellCommand.java:14: cannot find symbol
symbol : method redirectOutput(java.io.File)
location: class java.lang.ProcessBuilder
builder.redirectOutput(combinedFile);
I found a related question. To summarize the useful things from a couple answers found there, file redirection requires a shell, but exec doesn't have a shell context. Luckily, you can execute processes with redirection using ProcessBuilder. For your case, that would look something like:
public static void main(String[] args)
{
try{
ProcessBuilder builder = new ProcessBuilder("cat", "file1","file2");
File combinedFile = new File("combinedFile");
builder.redirectOutput(combinedFile);
builder.redirectError(combinedFile);
Process p = builder.start();
} catch(IOException e){
//handle exception...
}
}
Note: You may receive an error when calling redirectError or redirectOutput stating that the symbol cannot be found. This will occur if you are compiling against a version of Java before 1.7, since 1.7 is when these methods were introduced. If it is possible to upgrade your Java, doing so will eliminate this error.
If it is not possible to upgrade Java, the following code will work:
public static void main(String[] args)
{
try{
ProcessBuilder builder = new ProcessBuilder("cat", "file1","file2");
File combinedFile = new File("combinedFile");
Process p = builder.start();
InputStream isFromCat = p.getInputStream();
OutputStream osCombinedFile = new FileOutputStream(combinedFile);
byte[] buffer = new byte[1024];
int read = 0;
while((read = isFromCat.read(buffer)) != -1) {
osCombinedFile.write(buffer, 0, read);
}
} catch(IOException e){
//handle exception...
}
}
It is also probably worthwhile to note that making a system call to cat is not the optimal way to combine files from Java. I have been assuming this is a toy case to represent a more complex use case for you. If all you really want to do is combine two files, you should write your code to avoid system calls and just append the files by reading both in as input streams and then writing them out to the result file. If you're having trouble figuring that out, those details definitely belong in another question.
Related
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 4 years ago.
Improve this question
Goal: to initialise a JVM(2) from a separate JVM(1) using ProcessBuilder, capturing the resulting output from JVM(2) and displaying the result within a JTextArea in JVM(1).
Situation: able to launch JVM(2) from within JVM(1) and capture the resulting output from JVM(2) to a JTextArea within the JVM(1).
Problem: the JVM(2) will not respond to input until JVM(1) is terminated.
Thread inside VJM(1) that starts JVM(2):
Runnable runnable = () -> {
try {
JVMBooter.startSecondJVM();
} catch (Exception ex) {
Logger.getLogger(MyMenu.class.getName()).log(Level.SEVERE, null, ex);
}
};
Thread t = new Thread(runnable);
t.start();
JVMBooter source code:
import java.io.BufferedReader;
import java.io.File;
import java.io.InputStreamReader;
public class JVMBooter {
public static void startSecondJVM() throws Exception {
ProcessBuilder pb = new ProcessBuilder("cmd", "/c", "startscript.bat");
File dir = new File("D:/Server");
pb.directory(dir);
Process p = pb.start();
BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line = null;
( (line = reader.readLine()) != null && ! line.trim().equals("--EOF--")) {
OutputFrame.textArea.append(line + "\n");
}
}
}
The JVM(2) is started within the startscript.bat file with:
java -jar server.jar
Depending on the situation it may be necessary to read the error stream instead of the input stream, e.G. if your second java call is -version or the program you call only writes to stderr instead of stdout getting the Error Stream is the correct approach.
I wrote this MCVE:
import java.io.*;
public class Starter {
public static void main(String[] args) throws IOException {
ProcessBuilder pb = new ProcessBuilder("./dosomething.sh");
File dir = new File(new File(File.listRoots()[0], "tmp"), "2jvm");
pb.directory(dir);
Process p = pb.start();
BufferedReader read = new BufferedReader(new InputStreamReader(p.getErrorStream()));
String line;
while ( (line = read.readLine() ) != null) {
System.out.println("line: " + line);
}
}
}
and in 'dosomething.sh' this:
echo before
java -version
echo after
when I use p.getInputStream I would get the 'before' and 'after'. When I use p.getErrorStream I get the Java Version Information.
That might be true for you too. I suggest you add echo lines in your batch file to see if they get printed out.
I also wrote a simple hello world and when I called that from dosomething.sh it got printed to the input stream as expected. It is a weird quirk of -version to write to stderr.
For completeness here is the Hello World I used (it has waits to simulate a longrunning server process):
public class Caller {
public static void main(String[] args) {
synchronized(Caller.class) {
for(int ii = 0; ii < 10; ii++) {
try {
Caller.class.wait(1000);
} catch (Exception ex) {
ex.printStackTrace();
}
System.out.println("Hello world! " + ii);
}
}
}
}
I found this as one of the ways to run (using exec() method) python script from java. I have one simple print statement in python file. However, my program is doing nothing when I run it. It neither prints the statement written in python file nor throws an exception. The program just terminates doing nothing:
Process p = Runtime.getRuntime().exec("C:\\Python\\Python36-32\\python.exe C:\\test2.py");
Even this is not creating the output file:
Process p = Runtime.getRuntime().exec("C:\\Python\\Python36-32\\python.exe C:\\test2.py output.txt 2>&1");
What is the issue?
I think you could try your luck with the ProcessBuilder class.
If I read the Oracle documentation correctly, the std inputs and outputs are directed to pipes by default but the ProcessBuilder has an easy method for you to explicitly set output (or input) to a file on your system or something else.
If you want your Python program to use the same output as your Java program (likely stdout and stderr), you can use stg like this:
ProcessBuilder pb = new ProcessBuilder("C:\\Python\\Python36-32\\python.exe", "C:\\test2.py");
pb.redirectOutput(Redirect.INHERIT);
Process p = pb.start();
You can use the ProcessBuilder API, redirecting the output to a file and then wait for the result.
public class Main {
public static final String PYTHON_PATH = "D:\\Anaconda3\\python.exe";
public static final String PATH_TO_SCRIPT = "D:\\projects\\StartScript\\test.py";
public static void main(String[] args) throws IOException, InterruptedException {
ProcessBuilder builder = new ProcessBuilder();
builder.command(PYTHON_PATH, PATH_TO_SCRIPT);
// Redirect output to a file
builder.redirectOutput(new File("output.txt"));
builder.start().waitFor();
// Print output to console
ProcessBuilder.Redirect output = builder.redirectOutput();
File outputFile = output.file();
BufferedReader br = new BufferedReader(new FileReader(outputFile));
String st;
while ((st = br.readLine()) != null) {
System.out.println(st);
}
}
}
The python file test.py contains a simple print statement:
print("Hello from python")
I guess it would be even simpler, if you do not need to wait for the result.
Using the Process API should work, too.
Like in your example (I am using the same constants declared above):
Process p = Runtime.getRuntime().exec(PYTHON_PATH + " " + PATH_TO_SCRIPT);
p.waitFor();
byte[] buffer = new byte[1024];
byte[] errBuffer = new byte[1024];
p.getInputStream().read(buffer);
p.getErrorStream().read(errBuffer);
System.out.println(new String(buffer));
System.out.println(new String(errBuffer));
To see the output of the print statement, you need to wait and redirect the streams. Same for the error stream.
Now if you break the python script like this:
print("Hello from python')
you should be able to see the error printed as well.
One way to start a python process is using an entrypoint - test.cmd
echo Hello
python hello.py
here is hello.py
#!/usr/bin/env python3
import os
if not os.path.exists('dir'):
os.makedirs('dir')
Here is my Java code:
public static void main(String[] args) throws IOException {
try {
Process p = Runtime.getRuntime().exec("test.cmd");
p.waitFor();
Scanner sc = new Scanner(p.getInputStream());
while(sc.hasNextLine()){
System.out.println(sc.nextLine());
}
sc.close();
} catch (Exception err) {
err.printStackTrace();
}
}
I am trying to run wc -l filename command using Runtime.getRuntime().exec(command) and get the total number of lines. But it is not working.
Here is my code :
private String executeCommand(String command) {
StringBuffer output = new StringBuffer();
Process p;
try {
System.out.println(command);
p = Runtime.getRuntime().exec(command);
BufferedReader reader = new BufferedReader(
new InputStreamReader(p.getInputStream()));
String line = "";
while ((line = reader.readLine())!= null) {
output.append(line.split("\\s+")[0] + "\n");
}
int exc = p.waitFor();
System.out.println(exc);
} catch (Exception e) {
e.printStackTrace();
}
return output.toString();
}
exc (the exit value) is coming out zero. When I am printing the command which I am passing, it gives the right command. I have also tried to copy that command and run it in Linux. It's working, but not through program.
Command which I am passing to command variable in the functionexecuteCommand is:
wc -l < log1
Total number of lines it contains around 4597110. Also I dont want the size of file in bytes, I have to get the total number of lines only.
An easier alternative for getting the lines (with Java 8).
long lines = Files.lines(Paths.get("foo.txt")).count();
Kayaman has a much better answer if you're on Java 8 or better. If, however, you're stuck on an earlier iteration, see below.
You see this bit of code that you use for evaluating the output of wc:
while ((line = reader.readLine())!= null) {
output.append(line.split("\\s+")[0] + "\n");
}
That's remarkably similar to the code that could just read the file directly and count the number of lines.
So I'm not entirely certain why you think there's a need to call an external program to do this task, especially as it degrades the portabilty of your code.
Just open the file and read the lines, adding one to a counter for each line. And there you have it, the line count.
See, for example, the following file:
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.File;
public class Test
{
static private int getLineCount(String fspec) {
String line;
int count = 0;
try {
BufferedReader reader = new BufferedReader(
new FileReader(new File(fspec)));
while ((line = reader.readLine())!= null)
count++;
} catch (Exception e) {
count = -1;
}
return count;
}
public static void main(String[] args) {
System.out.println (getLineCount("/tmp/tempfile"));
}
}
Running this gives 140 as does the output from wc -l:
pax> wc -l /tmp/tempfile
140 /tmp/tempfile
Looks like you don't pass correct path to the file, try this when you run method:
URL fileName = ClassLoader.getSystemClassLoader().getResource("filename");
System.out.println(executeCommand("wc -l " + fileName.getPath()));
Try using Process in the following way:
ProcessBuilder pb = new ProcessBuilder("wc", "-l");
try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));){
Process process = pb.start();
while (reader.readLine() != null){
// you need to figure out in which line the
// relevant output is and parse that to int
// just look at some sample outputs of wc -l
}
int err = process.waitFor();
if(err!=0) {
// your code for processing the number of lines goes here
// the command finished without error
}
else {
// your code here in case the command had an error
}
} catch (Exception e){
// catch an Exception like IOException or InterruptedException here
}
Basically the Reader will read the output from the command that would go to the terminal when executing the command there.
You can use apache common io FileUtils.readLines(File file)
FileUtils.readLines(new File("filename.txt")).size()
This question already has answers here:
Start CMD by using ProcessBuilder
(3 answers)
Closed 9 years ago.
I would like to know if its possible to run cmd through Java. Not just one command at a time but a continuous stream of user input commands that relays the info received. Is this possible or should I just stop trying now?
I'm not sure why I'm attaching this; it's not very relevant, but this is what I was trying to accomplish this with. However, it resets the cmd after every command. (Yes, I realize this is bad coding, but I'm just attempting something my boss asked about.)
import java.io.*;
import java.util.Scanner;
public class Cmd {
public static void main(String[] args) throws IOException {
String line;
while (true) {
Scanner scanner = new Scanner(System.in);
String comm = scanner.nextLine();
ProcessBuilder builder = new ProcessBuilder("cmd.exe", "/c", comm);
builder.redirectErrorStream(true);
Process p = builder.start();
BufferedReader r = new BufferedReader(new InputStreamReader(p.getInputStream()));
while (true) {
line = r.readLine();
if(line==null){break;}
System.out.println(line);
}
}
}
}
Basically, cmd but behind a Java GUI with user input commands is my end game. If anyone could tell me if this is possible and if so point me in the right direction I would be grateful.
Yes, it is possible.
At the end of my answer is an example program. It is far from perfect and is missing some implementation details. For example proper exception handling and also detect when the cmd has exited... But it may be used as a starting point.
In essence the solution to your question is to start cmd.exe as a new Process. Then read commands in java from standard input stream (System.in) and pipe it to the cmd.exe-process. To provide feedback to the user you must read the standard output from cmd.exe-process and print it to the standard output of your java process (System.out). Also read standard error from cmd.exe-process and print it to standard error of your java process (System.err).
Close all resources when you are done. I indicated this in the example, but this is not production ready. Any exception would prevent the example program from cleaning up.
Another implementation detail: The example program uses a second thread to read output from cmd.exe-process. Otherwise you will only get output when the user hits enter.
Finally, here is the code:
package com.example;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
public class JavaCmd {
/**
* #param args
* #throws IOException
*/
public static void main(String[] args) throws IOException {
ProcessBuilder procBuilder = new ProcessBuilder("cmd.exe");
Process proc = procBuilder.start();
PrintWriter outToProc = new PrintWriter(proc.getOutputStream());
final BufferedReader inFromProc = new BufferedReader(new InputStreamReader(proc.getInputStream()));
final BufferedReader errorFromProc = new BufferedReader(new InputStreamReader(proc.getErrorStream()));
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
Thread outputThread = new Thread(new Runnable(){
#Override
public void run() {
while(true) {
try {
while(inFromProc.ready()) {
String line = inFromProc.readLine();
System.out.println(line);
}
while(errorFromProc.ready()) {
String line = errorFromProc.readLine();
System.err.println(line);
}
} catch (IOException e) {
throw new RuntimeException("Error in output thread", e);
}
try {
Thread.sleep(100);
} catch(InterruptedException e) {
System.out.println("Output Thread interrupted -> Thread will terminate");
break;
}
}
}
});
outputThread.start();
System.out.println("\n\nProxy shell is ready. Enter 'quit' to leave program.\n\n");
System.out.flush();
String line = null;
while((line = reader.readLine()) != null) {
if(line.equalsIgnoreCase("quit")) {
System.out.println("Good Bye");
break;
}
outToProc.println(line);
outToProc.flush();
}
reader.close();
outputThread.interrupt();
proc.destroy();
}
}
I know about using Runtime.exec, you pass it a native program to run + arguments. If it's a regular program, you can run it directly. If it's a shell script, you have to run an external shell program like sh or csh or cmd.exe.
Is there some Java class (either standard or open-source) that implements a shell, meaning a program that you pass a command string or a script into, that executes commands and redirects standard I/O/err accordingly, so that you could pass a string like foo | bar > baz.out in, and it would run the foo and bar programs w/o having to run another executable outside of Java?
(and by shell I don't mean BeanShell or the standalone Rhino Javascript interpreter, those are Java implementations to execute Java and Javascript code. I'm talking about Java implementations to execute non-Java executables and handle the plumbing of redirecting I/O.)
Ok, I've worked it out:
Basically, you need to invoke bash with a "-s" and then write the full command string to it.
public class ShellExecutor {
private String stdinFlag;
private String shell;
public ShellExecutor(String shell, String stdinFlag)
{
this.shell = shell;
this.stdinFlag = stdinFlag;
}
public String execute(String cmdLine) throws IOException
{
StringBuilder sb = new StringBuilder();
Runtime run = Runtime.getRuntime();
System.out.println(shell);
Process pr = run.exec(cmdLine);
BufferedWriter bufWr = new BufferedWriter(
new OutputStreamWriter(pr.getOutputStream()));
bufWr.write(cmdLine);
try
{
pr.waitFor();
} catch (InterruptedException e) {}
BufferedReader buf = new BufferedReader(
new InputStreamReader(pr.getInputStream()));
String line = "";
while ((line = buf.readLine()) != null)
{
sb.append(line + "\n");
}
return sb.toString();
}
}
Then use it like this:
ShellExecutor excutor = new ShellExecutor("/bin/bash", "-s");
try {
System.out.println(excutor.execute("ls / | sort -r"));
} catch (IOException e) {
e.printStackTrace();
}
Obviously, you aught to do something with the error string but this is a working example.
Since JDK 1.5 there is java.lang.ProcessBuilder which handles std and err streams as well. It's sort of the replacement for java.lang.Runtime
You've always been able to handle streams with Runtime.exec
e.g.
String cmd = "ls -al";
Runtime run = Runtime.getRuntime();
Process pr = run.exec(cmd);
pr.waitFor();
BufferedReader buf = new BufferedReader(new InputStreamReader(pr.getInputStream()));
String line = "";
while ((line=buf.readLine())!=null) {
System.out.println(line);
}
However, if you want to put shell characters such as pipe and redirect in there you'd have to write your own command line parser which links up the streams. As far as I know there hasn't one been written. That being said, could you just invoke bash from Java with a -c "ls | sort" for example and then read the input. Hmm time to do some testing.
You can use the ProcessBuilder API provided by java.
Runtime.getRuntime().exec(...) take either an array of strings or a single string. The single-string overloads of exec() will tokenise the string into an array of arguments, before passing the string array onto one of the exec() overloads that takes a string array. The ProcessBuilder constructors, on the other hand, only take a varargs array of strings or a List of strings, where each string in the array or list is assumed to be an individual argument. Either way, the arguments obtained are then joined up into a string that is passed to the OS to execute.
Find more details at the below link
Difference between ProcessBuilder and Runtime.exec()
Sample program to execute the commands.
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.lang.reflect.Field;
import java.util.List;
public class ProcessBuilderTest {
static ProcessBuilder processBuilder = null;
static Process spawnProcess = null;
static int exitValue;
static int pid;
static List<String> commands;
public static void main(String[] args) {
runSqoop();
}
public static void runSqoop() {
String[] commands = { "ssh", "node", "commands" };
processBuilder = new ProcessBuilder(commands);
try {
System.out.println("Executing " + commands.toString());
spawnProcess = processBuilder.inheritIO().start();
try {
exitValue = spawnProcess.waitFor();
pid = getPID(spawnProcess);
System.out.println("The PID is " + pid);
} catch (InterruptedException e) {
System.out.println(e.getMessage());
} catch (Exception e) {
System.out.println(e.getMessage());
}
System.out.println("Process exited with the status :" + exitValue);
} catch (Exception e) {
e.printStackTrace();
}
}
public static int getPID(Process process) {
try {
Class<?> processImplClass = process.getClass();
Field fpid = processImplClass.getDeclaredField("pid");
if (!fpid.isAccessible()) {
fpid.setAccessible(true);
}
return fpid.getInt(process);
} catch (Exception e) {
System.out.println(e.getMessage());
return -1;
}
}
}