Search code examples
javaprocessbuilder

Issues Using ProcessBuilder


I'm having some issues with ProcessBuilder.start();.

Here's an example of what the code looks like:

List<String> listOfStrings = new ArrayList<String>();
myListOfString.add("zip");
myListOfString.add("-r");
myListOfString.add(destinationPath);
myListOfString.add(newFilePath);

File zipFile = new File(workingDirectory, fileName + ".zip");

ProcessBuilder processBuilder = new ProcessBuilder(listOfStrings);
prcoessBuilder.directory(workingDirectory);
try{
  Process p = processBuilder.start();
  ...
  ...
  ...

workingDirectory has been verified to be a working directory using workingDirectory.isDirectory().

The issue occurs at Process p = processBuilder.start(); where it throws an IOException.

Below is the stacktrace:

java.io.IOException: Cannot run program "zip" (in directory "<Path to workingDirectory>"): error=2, No such file or directory
    at java.lang.ProcessBuilder.start(ProcessBuilder.java:1048)
    ...
    ...
    ...

workingDirectory does not point to a specific file (E.g.: /path/to/, not /path/to/file.zip), however, it is a valid directory. Is that possibly the issue?

I cannot give the exact code due to the nature of the project, but I can't imagine the input matters too much if it's crashing at Process p = processBuilder.start();, however, this is why I'm reaching out so it might be the case.

Let me know if any clarification is needed.


Solution

  • The error message says that Java does not find an executable called zip in the current directory or on the path, which can be seen with:

    String systemPath = System.getenv("PATH");
    System.out.println("PATH="+systemPath);
    

    Fix your launcher to include the full path to "zip" or fix the PATH used for your Java VM to include a directory that contains zip.

    myListOfString.add("/the/path/to/zip");
    

    or something like this (depending on your shell / terminal)

    export PATH=/the/path/to:$PATH
    

    As @Abra suggests, there are better ways to ZIP inside Java without relying on ProcessBuilder such as:

    public static void zip(Path dir, Path zip) throws IOException
    {
        Map<String, String> env = Map.of("create", "true");
        try (FileSystem fs = FileSystems.newFileSystem(zip, env))
        {
            // This predicate processed the action because it makes use of BasicFileAttributes
            // Rather than process a forEach stream which has to call Files.isDirectory(path)
            BiPredicate<Path, BasicFileAttributes> foreach = (p,a) -> {
                copy(p,a, fs.getPath("/"+dir.relativize(p)));
                return false;
            };
    
            Files.find(dir, Integer.MAX_VALUE, foreach).count();
        }
        System.out.println("ZIPPED "+dir +" to "+zip);
    }
    private static void copy(Path from, BasicFileAttributes a, Path target)
    {
        System.out.println("Copy "+(a.isDirectory() ? "DIR " : "FILE")+" => "+target);
        try
        {
            if (a.isDirectory())
                Files.createDirectories(target);
            else if (a.isRegularFile())
                Files.copy(from, target, StandardCopyOption.REPLACE_EXISTING);
        }
        catch (IOException e)
        {
            throw new UncheckedIOException(e);
        }
    }