1

I have 2 folders, each containing dozens of batch files (*.bat).

The batch files containing text similar to either

del /f/q F:\MEDIA\IMAGE99\2010\270\z\4034\123.tif > nul
del /f/q F:\MEDIA\IMAGE99\2010\266\z\3025\456.tif > nul
del /f/q F:\MEDIA\IMAGE99\2010\267\z\3025\789.tif > nul
del /f/q F:\MEDIA\IMAGE99\2010\286\z\9025\101.tif > nul
del /f/q F:\MEDIA\IMAGE99\2010\272\z\6029\112.tif > nul
del /f/q F:\MEDIA\IMAGE99\2010\258\z\4034\134.tif > nul

or

rmdir /q F:\MEDIA\IMAGE99\2010\270\z\4034
rmdir /q F:\MEDIA\IMAGE99\2010\266\z\3025
rmdir /q F:\MEDIA\IMAGE99\2010\267\z\3025
rmdir /q F:\MEDIA\IMAGE99\2010\286\z\9025
rmdir /q F:\MEDIA\IMAGE99\2010\272\z\6029
rmdir /q F:\MEDIA\IMAGE99\2010\258\z\4034

In Java, I list each batch File in each folder, and cycle through the list, executing each batch file as follows:

public static boolean batch(File file) {
    
    boolean handled = false;
    Process process = null;
    try {

        process = Runtime.getRuntime().exec("cmd /c start " + file);
        handled = process.waitFor() == 0;

    } catch (Exception ex) {
        // handling removed for example purposes
    } 
    
    return handled;
}

After the method returns, I delete the batch file.

The problem is, none of the commands within my batch files run (the files and folders I request deleted or removed are not), and the Java process simply continues and deletes batch file itself.

The batch files are located in folder d:\working\spaced folder\purge\batch_files\

Having written this out, I suspect my problem is that I'm passing a file path with a space in it to the exec() method.

Is my suspicion correct? If so, how do I resolve it? If not, what might the problem be?

I'm going to look into Java: Execute /cmd /c start path-with-spaces\program.exe now that I've considered it.


UPDATE

Per the comments below, I've changed my code, but now the output hangs at waitFor() and the batch file is not being processed (the files I request deleted are still there).

Code:

        String commandString = "cmd /c \"" + file +"\"";
        logger.info("COMMAND " + commandString);
        process = Runtime.getRuntime().exec(commandString);
        logger.info("WAITING FOR " + commandString);
        handled = process.waitFor() == 0;
        logger.info("HANDLED " + commandString + " = " + handled);

Output:

COMMAND : cmd /c "d:\working\spaced folder\purge\deleteBatch\F_140.bat"
WAITING FOR : cmd /c "d:\working\spaced folder\purge\deleteBatch\F_140.bat"
Community
  • 1
  • 1
JoshDM
  • 4,939
  • 7
  • 43
  • 72
  • 2
    if your batch file path has a space in it, then yes, you'll be doing `del c:\foo bar`, when it should be `del "c:\foo bar"`. put some quotes around the `file` parameter – Marc B Jan 27 '14 at 21:48
  • 2
    You probably don't want to use the `start` command, because if you do, the call `.waitFor` won't wait for the batch file to finish. – Harry Johnston Jan 27 '14 at 22:05
  • It seems to work slower with the quotes, but I'm not seeing any deletions of files from the internal batch calls. I will try without `start` because I think that might be the problem. I originally didn't have `start`, but that's what I get for reading StackOverflow for answers first. :P – JoshDM Jan 27 '14 at 22:08
  • No, now it's just hanging at processing the file. Not sure how far it's getting; adding debug. – JoshDM Jan 27 '14 at 22:31
  • Is there a reason you don't want to just delete the files in Java? Would be much easier than a bunch of inter-process stuff. – jpmc26 Jan 27 '14 at 22:48
  • It's several million files total and it seems to take a while calling `File.delete()`. – JoshDM Jan 27 '14 at 22:50
  • It might take a while in cmd, too, then. Could you give us more detail about the app as a whole? If just deleting these files is the end goal, then you might be better served by PowerShell or VB or maybe even just cmd. – jpmc26 Jan 27 '14 at 22:56
  • 1
    No. Removing the directory via cmd.exe will be many times faster than doing it in Java. Windows has a wildcard delete system call: Java doesn't. @OP I would get rid of the 'start' from the command. You do want to know whether it worked or not, and you won't if you use 'start'. – user207421 Jan 27 '14 at 23:16
  • I've already proven to myself that doing it through `cmd` works faster; this is several million files across several extended drives; we're hitting each one with 100 deletes, and then after all deletes are done, folder removal commences. – JoshDM Jan 27 '14 at 23:25
  • @jpmc26 - I receive a list of several million files across 12 extended drives. After sanitizing the incoming data, I create a file which I parse. Deleting each individual file with a File.delete call, line by line, even multi-threaded, can take an hour to even get past 10k. If too much emphasis is given to any one extended drive, it starts to seize. By transforming the lines into individual batch files of 100 lines per drive, and calling each sorted round-robin with no drive called more than once at a given time, I've already processed 3 million since last night. – JoshDM Jan 28 '14 at 14:33
  • @jpmc26 - the other thing to do is to bundle the file deletes and the folder removals separately; delete, then immediate removal seems to lag, whereas a file of deletes, then a file of removals works. I have a folder of file delete batch files and a folder of folder removal batch files. – JoshDM Jan 28 '14 at 19:55
  • I assume the sanitizing is done by the application you're developing. Why not do the file clean up outside the app and call your app and the file deletion via a script? Seems like a simpler setup. Right tool for the right job and all that. (On a side note, I didn't say it was just as quick inside Java. I just said it might still take a while.) – jpmc26 Jan 28 '14 at 21:02

2 Answers2

3

now the output hangs at waitFor()

When you start an external process from Java using Runtime.exec you must read any output that the process produces otherwise the process may block (source: JavaDocs for java.lang.Process).

Use ProcessBuilder instead, and call redirectErrorStream to merge the standard output and error streams, then read all the content from process.getInputStream() until you reach EOF. Only then is it safe to call waitFor.

ProcessBuilder will also help with the spaces issue, as you must split up the command line into individual words yourself

ProcessBuilder pb = new ProcessBuilder("cmd", "/c", file.getAbsolutePath());
Ian Roberts
  • 120,891
  • 16
  • 170
  • 183
  • I'll try that right now, thanks. FYI, `ProcessBuilder pb = new ProcessBuilder("cmd", "/c", file.toString());` – JoshDM Jan 27 '14 at 23:00
  • @JoshDM if it's a `java.io.File` then `getAbsolutePath` is safer than `toString` as it will give you the full path even if the `File` itself is relative. – Ian Roberts Jan 27 '14 at 23:03
  • Thank you. This helped me resolve the issue. Checking as answer; code for solution used is posted in separate answer. – JoshDM Jan 28 '14 at 14:29
0

Code of successful solution used, based on Ian Roberts' answer:

Uses Apache Commons-IO

package com.stackoverflow.windows;

import java.io.File;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.output.NullOutputStream;

public class Command {

    private Command() {}

    public static boolean batch(File file) {
    
        boolean handled = false;
        Process process = null;
        ProcessBuilder pb = new ProcessBuilder("cmd", "/c", file.getAbsolutePath());
        pb.redirectErrorStream(true);
    
        try {

            process = pb.start();
            IOUtils.copy(process.getInputStream(), new NullOutputStream());
            handled = process.waitFor() == 0;
            
        } catch (Exception ignore) {
        
            // Only throws an IOException we're trying to avoid anyway, 
            // and an expected InterruptedException 
            // handled will be false
        
        } finally {

            if (process != null) {
        
                IOUtils.closeQuietly(process.getInputStream());
            }           
        }
                
        return handled;
    }
}
Community
  • 1
  • 1
JoshDM
  • 4,939
  • 7
  • 43
  • 72