I'm trying to copy a bunch of files with a specific extension from one folder to another using the copy command, heres wat im doing,
String[] command = new String[3];
command[0] = "cmd";
command[1] = "/c";
command[2] = "copy C:\\output\\html\\*.txt C:\\output\\";
ProcessBuilder copyFiles = new ProcessBuilder(command);
p = copyFiles.start();
p.waitFor();
the thing is, this code works fine for files less than some 5 or so, but just stops responding wen the number of files are more (even for 15 files) !! and the files are not copied either!!
I dont know what the problem is, will be glad if someone could help! :)
You're not reading the output the copy command is generating.
When spawning a child process using ProcessBuilder, output generated by your child process gets written to a buffer. If this buffer isn't read from, it eventually fills up. When it fills up, the copy command can't write any more to it and so is blocked by the operating system. It is then forced to wait until space is made in the buffer by reading from it.
I ran your code with 20 files and I found that it did indeed hang.
One way to solve your problem is to redirect the output from copy to NUL. Most of the output from copy is a list of all the files it has copied, which you probably don't care too much for. To do this redirection, modify the line that assigns to command[2] to the following:
command[2] = "copy C:\\output\\html\\*.txt C:\\output\\ >NUL 2>NUL";
However, if there is a problem copying files, you might not know about it if you do this.
Alternatively, you can read the output that the copy command generates. The following code sends it to System.out, but you can easily send it elsewhere or completely ignore it if you wish:
String[] command = { "cmd", "/c", "copy C:\\output\\html\\*.txt C:\\output\\" };
ProcessBuilder copyFiles = new ProcessBuilder(command);
copyFiles.redirectErrorStream(true);
p = copyFiles.start();
// The InputStream we get from the Process reads from the standard output
// of the process (and also the standard error, by virtue of the line
// copyFiles.redirectErrorStream(true) ).
BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line;
do {
line = reader.readLine();
if (line != null) { System.out.println(line); }
} while (line != null);
reader.close();
p.waitFor();
I gave each approach a quick test with the same 20 files and neither approach hung.
EDIT: You might also want to try a 'hybrid' approach, by throwing away what copy writes to standard output (e.g. the list of files it's copying) but using the second approach to read in what it writes to standard error (e.g. error messages). To do this, you'd add the >NUL, which redirects the standard output of copy to NUL, but you wouldn't add the 2>NUL, since that redirects standard error to NUL.
Related
I'm trying to start some .jar inside an .jar using this code: (I'm trying with craftbukkit server right know)
Runtime rt = Runtime.getRuntime();
Process p = rt.exec("java -jar craft.jar");
BufferedReader out = new BufferedReader(new InputStreamReader(p.getInputStream()));
BufferedWriter in = new BufferedWriter(new OutputStreamWriter(p.getOutputStream()));
this.running = true;
while(this.running){
String line;
if((line = out.readLine()) != null){
System.out.println(line);
}
}
The problem is, it work for a moment, but after a while it stops outputting or just print '>' as show in the image below. How can I fix this? **Plus, Which is the correct way to send commands to the bukkit server?
Thanks!
When using a Process in Java, it is crucial to read stdout and stderr in two independend threads (your while loop). Otherwise the child process can lock up waiting for the filled buffer to be drained.
With Java 7 you can actually redirect stderr to stdout, and possibly redirect the resulting stream to the parents stdout. With older versions you need to attach the seperate threads.
Besides: you should not use the "single string" variant of exec, but specify the args as arrays. This is safer (less vulnerable to parsing problems).
(But I am not sure if this is your actual problem. Maybe you should dump the error stream and see if it helps)
I do it like that:
#Override
public void run(){
// ...
BufferedReader stdInput = new BufferedReader(new InputStreamReader(proc.getInputStream())); //of course proc is an instance of Process, and it's set up
while(true){
String line = stdInput.readLine();
if(line == null) break; //this is essential, when process terminates, line gets null
System.out.println(">"+line);
}
// ...
}
Give it a try.
(Technically it's similiar how you did, but it works for me so i share it)
I am trying to expand a zip file using 7zip but I keep getting the 7zip Usage printout.
The zip exist in c:\temp
The same command succeed in batch window :
C:\TEMP>7z x "tryThis.zip"
I tried adding the workdir path to the file,And also without the working dir,
nothing help. - I can probably run this using CMD/c command but I prefer to keep the code clean
What am I doing wrong?
Thank you!
String pathTo7ZipExe = "c:\\program files\\7-zip\\7z.exe";
String fileName ="tryThis.zip";
String workingDir = "c:\\temp\\";
Process process = Runtime.getRuntime().exec(
new String[]{pathTo7ZipExe},
new String[]{" x \"" + fileName +"\""},
new File(workingDir));
BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while ((line = in.readLine()) != null) {
System.out.println(line);
}
// wait for zip to end.
int exitVal = process.waitFor();
Please have a look at the documention for Runtime.exec
What you were actually trying to do is calling 7-zip without arguments and providing the arguments as your environment. Environment is something like Windows PATH etc.
so you would probably want to do something like:
Runtime.getRuntime().exec(new String[]{pathToZipExe, "x", fileName}, null, new File(workingDir));
On the other hand I would strongly advise to have a look on ZipInputStream which is included in java - using that you can also unpack zip files.
Cheers
You're invoking the overload of exec which accepts envp array as the second argument. This envp array is not for arguments at all, so actually you don't pass any arguments: that's why you get the usage printout.
Quotes and spaces aren't themselves part of arguments: they are used for separation into argv (with minor reservations, it's also true for Windows: that's how CommandLineToArgW works, even though full original command line with quotes and spaces is always available).
So it should be something like this:
Runtime.getRuntime().exec(new String[]{pathTo7ZipExe, "x", fileName},
new String[]{}, new File(workingDir));
(too bad I don't know Java, so the code might be unidiomatic, but it should work).
I'm making an update function for my project, it's working great, until i want it to restart, basically I download the new file and replace it with the old one, and then i want to run it again, now for some reason it doesn't wna run, and i don't get any error...
Here is the complete update class:
http://dl.dropbox.com/u/38414202/Update.txt
Here is the method i'm using to run my .jar file:
String currDir = new File("(CoN).jar").getAbsolutePath();
Process runManager = Runtime.getRuntime().exec("java -jar " + currDir);
It's not clear to me, why do you need to run the jar with a call to exec() . Given that you need to run the code in the .jar file from a Java program, you could simply run the main() method as defined in the jar's manifest, and capture its output - wherever that is.
Using exec() is OK when you need to call a program from the underlying operating system, but there are easier ways to do this if both the caller and the callee are Java programs.
Now, if your jar is gonna change dynamically and you need to update your program according to a new jar, there are mechanisms for reloading its contents, for instance take a look ath this other post.
The JavaDocs for the Process class specifically point out that if you don't capture the output stream of the Process and promptly read it that the process could halt. If this is the case, then you wouldn't see the process that you started run.
I think you have to capture the stream like this :
BufferedReader stdInput = new BufferedReader(new InputStreamReader(runManager.getInputStream()),8*1024);
BufferedReader stdError = new BufferedReader(new InputStreamReader(runManager.getErrorStream()));
// read the output from the command
String s = null;
System.out.println("Here is the standard output of the command:\n");
while ((s = stdInput.readLine()) != null) {
System.out.println(s);
}
The exec function doesn't automatically lookup into the PATH to start a process, so you have to pass the complete path for the java binary.
You can do that by using the java.home system property, see this answer: ProcessBuilder - Start another process / JVM - HowTo?
No one here seemed to help me, so I went to ask my friend and I had it almost right. It abiously required the string to be an array.
solution:
String[] cmd = {"java", "-jar", currDir};
try {
Runtime.getRuntime().exec(cmd);
} catch (IOException e1) {
e1.printStackTrace();
}
Im developing a simple console Application using java. The code is given below
` try {
File file = new File("writer.txt");
writer = new BufferedWriter(new FileWriter(file));
Process myProcess = Runtime.getRuntime().exec("jps -l");
BufferedReader stdout = new BufferedReader(new InputStreamReader(
myProcess.getInputStream()));
String line = stdout.readLine();
while (line != null) {
if (line.contains(".jar")) {
writer.write(line);
System.out.println(line);
}
line = stdout.readLine();
}
writer.close();
}
`
The code will display the currently running the jar in my windows. The output format is displayed 2356 Timeout.jar I want to display it only Timeout.jar How to remove that integer values. Thanks in advance.
Assuming you have "2356 Timeout.jar" in line, this will return just the jar name:
line.substring(line.indexOf(" ") + 1);
I think there must be an easier way to get the running jar though. I did a quick search and you may want to look at these questions:
How to get the path of a running JAR file?
http://arstechnica.com/civis/viewtopic.php?f=20&t=261563
You could:
Apply a regular expression to line before writing it out. (Think start-of-line, then integers, ending at the first whitespace)
Use ls (or dir) as your exec process instead of jps
Just grab the directory listing directly instead of via the external process as per below:
File dir = new File("directoryName");
String[] children = dir.list();
Doing what you have via JPS is probably not a good idea if this isn't a quick one-off app or a learning exercise because of the following note from the jps man page:
NOTE- You are advised not to write scripts to parse jps output since
the format may change in future releases. If you choose to write
scripts that parse jps output, expect to modify them for future
releases of this tool.
Tokenizing the result is one way.
if you are in unix, use awk to get the second field.
If you are using a Linux based OS,
Instead of
Process myProcess = Runtime.getRuntime().exec("jps -l");
try this one
Process myProcess = Runtime.getRuntime().exec("jps -l | cut -d \" \" -f2");
I need to run a couple of other programs from my own Java program, basically I need to run these command line statements.
svn log --xml -v > svn.log
and
java -jar example.jar arg1 arg2
and I need to use the text outputs written to the console from these programs in my own program. I've tried Runtime.getRuntime().exec() with the svn, but it doesn't seem to be doing anything because it doesn't make a svn.log file. Also both programs need to be called in different places, the svn line needs to be called from inside one folder and the java line needs to be called from another.
Any ideas on how to go about this? If this is not possible in Java, is there a way to do it in C#?
Thanks
Here:
ProcessBuilder processbuilder
try
{
processbuilder.directory(file);
processbuilder.redirectErrorStream(true);
process = processbuilder.start();
String readLine;
BufferedReader output = new BufferedReader(new InputStreamReader(process.getInputStream()));
// include this too:
// BufferedReader output = new BufferedReader(new InputStreamReader(process.getErrorStream()));
while((readLine = output.readLine()) != null)
{
m_Logger.info(readLine);
}
process.waitFor();
}
I've used something similar. You'll actually want to do something with the readLine. I just copied and pasted from code where I didn't care what it said.
The redirection > (like the pipe |) is a shell construct and only works when you execute stuff via /bin/sh (or equivalent). So the above isn't really going to work. You could execute
/bin/sh -c "svn log --xml -v > svn.log"
and read svn.log.
Alternatively, you can read the output from the process execution and dump that to a file (if you need to dump it to a file, or just consume it directly as you read it). If you choose this route and consume stdout/stderr separately, note that when you consume the output (stdout), you need to consume stderr as well, and concurrently, otherwise buffers will block (and your spawned process) waiting for your process to consume this. See this answer for more details.
instead of piping in your command, just let it print to standard output and error output. You can access those streams from your process object that is returned from exec.
For the svn stuff use java SVNKit API.
Seeing your two commands, why don't you do it directly from Java, without executing ? You could use SVNKit for the svn part, and include directly the jars in your classpath.
Try this
public static void main(String[] args) {
try {
// Execute a command with an argument that contains a space
System.out.println(args[0]);
String[]commands = new String[]{"svn", "info", args[0]};
Process process = Runtime.getRuntime().exec(commands);
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
StringBuilder builder = new StringBuilder();
String line = null;
while ((line = reader.readLine()) != null) {
builder.append(line);
builder.append(System.getProperty("line.separator"));
}
String result = builder.toString();
System.out.println(result);
}catch(Exception e){
System.out.print(e);
}
}