CMD.exe command in java not terminating - java

I'm trying to use cmd.exe to search for a file in a specific directory and then display the path in a java program and write it to a file. The problem is that the process never terminates.
Here is my code:
String[] str = new String[] { "cmd.exe ", "cd c:\\",
" dir /b /s documents", "2>&1" };
Runtime rt = Runtime.getRuntime();
try{
Process p = rt.exec(str);
InputStream is =p.getInputStream();
InputStreamReader in = new InputStreamReader(is);
StringBuffer sb = new StringBuffer();
BufferedReader buff = new BufferedReader(in);
String line = buff.readLine();
while( line != null )
{
sb.append(line + "\n");
line = buff.readLine();
}
System.out.println( sb );
File f = new File("test.txt");
FileOutputStream fos = new FileOutputStream(f);
fos.write(sb.toString().getBytes());
fos.close();
}catch( Exception ex )
{
ex.printStackTrace();
}

Please try
cmd /c
instead of simply
cmd
Reference

Runtime.exec doesn't work that way. You can't pass multiple commands like that to cmd.exe.
Runtime.exec allows you to execute a single process with a list of arguments. It does not provide any "shell" operations (like 2>&1 for instance). You must do that sort of IO redirection yourself using the Input/Output streams.
It's similar to calling another program's main function.
You could try `Runtime.exec( new String[] { "cmd.exe", "/c", "dir", "C:\\" } );
But realistically, if you want file listings, you're much better off using the facilities in the java.io.File class, which won't depend on operating system specific features.

why are not using Java to do directory traversal instead of calling external shell command? It makes your code not portable!

You must use the start command in addition to the cmd.exe process with the /C or /K switch BEFORE the start command. Example: to convert the Windows's command interpreter in a bash console (from the mingw prroject) you must invoke the exec method of the Runtime class with the command "C:\Windows\System32\cmd.exe /C start C:\mingw\msys\1.0\bin\bash.exe" (I use an external command rather than an internal because it's more signifiant but you can use internal command like DIR and so on).

Related

java.lang.Runtime exception "Cannot run program"

I am getting an exception like java.io.IOException: Cannot run program cat /home/talha/* | grep -c TEXT_TO_SEARCH": error=2, No such file or directory while executing the command below despite that there are no issues when I execute the same command through the terminal. I need to execute and return the output of the command below:
cat /home/talha/* | grep -c TEXT_TO_SEARCH
Here is the method used to execute commands using Runtime class:
public static 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();
}
Runtime.exec does not use a shell (like, say, /bin/bash); it passes the command directly to the operating system. This means wildcards like * and pipes (|) will not be understood, since cat (like all Unix commands) does not do any parsing of those characters. You need to use something like
p = new ProcessBuilder("bash", "-c", command).start();
or, if for some bizarre reason you need to stick to using the obsolete Runtime.exec methods:
p = Runtime.getRuntime().exec(new String[] { "bash", "-c", command });
If you are only running that cat/grep command, you should consider abandoning the use of an external process, since Java code can easily traverse a directory, read lines from each file, and match them against a regular expression:
Pattern pattern = Pattern.compile("TEXT_TO_SEARCH");
Charset charset = Charset.defaultCharset();
long count = 0;
try (DirectoryStream<Path> dir =
Files.newDirectoryStream(Paths.get("/home/talha"))) {
for (Path file : dir) {
count += Files.lines(file, charset).filter(pattern.asPredicate()).count();
}
}
Update: To recursively read all files in a tree, use Files.walk:
try (Stream<Path> tree =
Files.walk(Paths.get("/home/talha")).filter(Files::isReadable)) {
Iterator<Path> i = tree.iterator();
while (i.hasNext()) {
Path file = i.next();
try (Stream<String> lines = Files.lines(file, charset)) {
count += lines.filter(pattern.asPredicate()).count();
}
};
}
$PATH is an environment variable that tells the system where to search for executable programs (it's a list of directories separated by colons). It is usually set in your .bashrc or .cshrc file but this is only loaded when you log in. When Java runs, $PATH is likely not set because the rc file is not executed automatically, so the system can't find programs without specifying exactly where they are. Try using /bin/cat or /usr/bin/cat instead of just cat and see if it works. If it does, $PATH is your problem. You can add $PATH=/bin:/usr/bin to your script or just leave it with the directory name specified (e.g. /bin/cat).
Just because you can execute it in a login session doesn't mean it will work the same when a daemon like your Java program runs. You have to know what's in your .bashrc or .cshrc file and even sometimes how the system file is written (/etc/bashrc) in order to know how to write a script that runs under a daemon. Another consideration is that daemons often run under the context of a different user, and that throws things off, too.

Process builder won't find my file

I am trying to compile a java program using the ProcessBuilder but everytime i see this error being present on the console even though the file is present at that path.
ERROR
java.io.IOException: Cannot run program "javac
/Users/foo/Desktop/online-compiler/user1455523443383/Main.java":
error=2, No such file or directory
#Override
public ProgramResult executeProgram(File program) throws IOException {
String parent = program.getParentFile().getParentFile().getAbsolutePath();
String[] commands = new String[]{
"javac "+program.getAbsolutePath(),
// "cd "+parent,
// "java -cp "+parent+" "+PACKAGE_NAME+"."+MAIN_CLASS
};
ProcessBuilder builder = new ProcessBuilder(commands);
builder.redirectErrorStream(true);
Process executorProcess = builder.start();
BufferedReader reader = new BufferedReader(new InputStreamReader(executorProcess.getInputStream()));
String line;
StringBuilder sb = new StringBuilder();
while((line = reader.readLine())!=null) {
sb.append(line);
}
reader.close();
ProgramResult result = new ProgramResult();
result.setOutput(sb.toString());
return result;
}
Some more information
Javac is on the path, as running it(without the file) via ProcessBuilder is printing the help options.
OS : MACOSX
Conclusions from this questions are
1) ProcessBuilder needs every argument to the command as a separate index , like to execute "javac filename.java" you write this
new String[] {"javac" , "filename.java"}
2) To execute multiple commands you should be using the following trick
new String[]{
"/bin/bash",
"-c",
"javac "+
program.getAbsolutePath()+
" &&" +
" java -cp " +
parent +
" "+ PACKAGE_NAME+"."+MAIN_CLASS,
}
A big thanks to #kucing_terbang for really digging in this problem with me to solve it.
AFAIK, if you want to put an argument into the ProcessBuilder, you should put in into another index of the array.
So, try change the command variable into something like this and try again.
String[] commands = new String[]{"javac", program.getAbsolutePath()};
If you want to compile a Java class, better use JavaCompiler acquired from ToolProvider.getSystemJavaCompiler();
JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();
javaCompiler.run(null, null, null, program.getAbsolutePath());

Executing custom Commands in Java

With this, we can execute "build in" commands in java. However if we want to run some custom commands from this, Changing "pwd" to "device_id -l" doesn't work. "device_id -l" should list all the ids of attached devices of currently host. if "device_id -l" is executed in terminal itself. it works fine. There is not a question for the "build in" bash commands. Thank you.
String cmd = "pwd";
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);
We can excuate
You can try using ProcessBuilder.
// create process
ProcessBuilder pb = new ProcessBuilder("/bin/bash", "-c", "device_id", "-l");
// start process
Process p = pb.start();
// wait for process exit
p.waitFor();
// read process output
BufferedReader buf = new BufferedReader(newInputStreamReader(p.getInputStream()));
String line = "";
while ((line=buf.readLine())!=null)
System.out.println(line);
You need to split your command+arguments to a String array. In your case, if you want to execute "device_id -l", split that into an array like this:
String[] cmd = new String[] {"/full/path/to/device_id", "-l"};
Process pr = Runtime.getRuntime().exec(cmd);
And, you might want to use ProcessBuilder.
String[] cmd = new String[] {"/full/path/to/device_id", "-l"};
ProcessBuilder pb = new ProcessBuilder(cmd);
Process pr = pb.start();
Finally, you'll have to take into account that Java does not look for executables in PATH (like command shell does), you'll have to provide full path to the executable/script that you want to execute (or it has to be in the working directory; you can set the working directory with ProcessBuilder.directory(File)).
See also: Difference between ProcessBuilder and Runtime.exec()

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".

How do I Pipe process output to a file on Windows and JDK 6u45

I have the following windows batch file (run.bat):
#echo off
echo hello batch file to sysout
And the following java code, which runs the batch files and redirects output to a file:
public static void main(String[] args) throws IOException {
System.out.println("Current java version is: " + System.getProperty("java.version"));
ProcessBuilder pb =
new ProcessBuilder("cmd.exe", "/c",
"run.bat"
,">>", "stdout.txt","2>>", "stderr.txt"
);
System.out.println("Command is: " + pb.command());
Process proc = pb.start();
InputStream in = proc.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
String line = null;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
int exitValue = proc.exitValue();
System.out.println("Exit value: " + exitValue);
}
On JDKs up to and including JDK6u43 I get the following output:
Current java version is: 1.6.0_29
Command is: [cmd.exe, /c, run.bat, >>, stdout.txt, 2>>, stderr.txt]
Exit value: 0
and the script output is written to the file.
As of JDK 6u45 and 7, I get the following output:
Current java version is: 1.6.0_45
Command is: [cmd.exe, /c, run.bat, >>, stdout.txt, 2>>, stderr.txt]
hello batch file to sysout
Exit value: 0
And nothing is written to the output file.
This may or may not be related to the changes made in Runtime.exec() , described at: http://www.oracle.com/technetwork/java/javase/6u45-relnotes-1932876.html
What is the correct way of starting a process on Windows with output redirected to files?
Note: In a real world scenario, the command to execute may include parameters with spaces, as in:
ProcessBuilder pb = new ProcessBuilder("cmd.exe", "/c",
"run.bat", "Some Input With Spaces",
">>", "stdout.txt","2>>", "stderr.txt");
This is the simplest method i found on http://tamanmohamed.blogspot.in/2012/06/jdk7-processbuilder-and-how-redirecting.html
File output = new File("C:/PBExample/ProcessLog.txt");
ProcessBuilder pb = new ProcessBuilder("cmd");
pb.redirectOutput(output);
Several suggestions here:
Does the input with the spaces need to be treated as single String (with spaces),or id it in actual several inputs? If the first Option is the case I would suggest to quote it for the windows runtime:
ProcessBuilder pb = new ProcessBuilder("cmd.exe", "/c",
"run.bat", "\"Some Input With Spaces\"",
">>", "stdout.txt","2>>", "stderr.txt");
Instead of redirecting the input to stdout.txt and stderr.txt using the shell, why not do it using Java using getOutputStream() and getErrorStream()? Here is an example using Guava's IO package. Of course you may want to have those in separate threads, you need proper exception handling, etc.
InputStream stdout = new BufferedInputStream(proc.getInputStream());
FileOutputStream stdoutFile = new FileOutputStream("stdout.txt");
ByteStreams.copy(stdout, stdoutFile);
InputStream stderr = new BufferedInputStream(proc.getErrorStream());
FileOutputStream stderrFile = new FileOutputStream("stderr.txt");
ByteStreams.copy(stderr, stderrFile);
stdout.close();
stderr.close();
stdoutFile.close();
stderrFile.close();
Another option, why not create a run.bat wrapper that will make the redirections?
#echo off
cmd.exe /c run.bat "%1" >> "%2" 2>> "%3"
Use getOutputStream() on the process, instead of using System.out.println(). Sometimes the semantics change between Java implementations.
This seems to be a bugfix actually - the newer implementation makes sense.

Categories