ProcessBuilder против Runtime.exec()

Я пытаюсь создать внешнее приложение на Java для обработки пакетных преобразований SVG с использованием функции командной строки Inkscape. Я беру и обновляю код с сайта https://sourceforge.net/projects/conversionsvg/. Способ, которым первоначальный разработчик обрабатывал вызов Inkscape с помощью Runtime.getRuntime().exec(String). Проблема, с которой я сталкиваюсь, заключается в некоторых несоответствиях между использованием метода A и метода B. Я создал простой тестовый проект Java, чтобы продемонстрировать различные выполняемые действия.

CallerTest.java

package conversion;

import java.io.IOException;

public class CallerTest {
  
    static String pathToInkscape = "\"C:\\Program Files\\Inkscape\\inkscape.exe\"";  
    
    public static void main(String[] args) {
      
      ProcessBuilderCaller processBuilder = new ProcessBuilderCaller();
      RuntimeExecCaller runtimeExec = new RuntimeExecCaller();
      
      // methodA() uses one long command line string
      try {
        
        String oneLongString_ProcessBuilder = pathToInkscape + " -f \"C:\\test.svg\" -D -w 100 -h 100 -e \"C:\\ProcessBuilder-methodB.png\"";
        String oneLongString_RuntimeExec =    pathToInkscape + " -f \"C:\\test.svg\" -D -w 100 -h 100 -e \"C:\\RuntimeExec-methodA.png\"";
        
//        processBuilder.methodA(oneLongString_ProcessBuilder);
        runtimeExec.methodA(oneLongString_RuntimeExec);
        
      } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
      }
      
      // methodB() uses an array containing the command and the options to pass to the command
      try {

        String[] commandAndOptions_ProcessBuilder = {pathToInkscape, " -f \"C:/test.svg\" -D -w 100 -h 100 -e \"C:\\ProcessBuilder-methodB.png\""};
        String[] commandAndOptions_RuntimeExec =    {pathToInkscape, " -f \"C:/test.svg\" -D -w 100 -h 100 -e \"C:\\RuntimeExec-methodB.png\""};
        
        processBuilder.methodB(commandAndOptions_ProcessBuilder);
//        runtimeExec.methodB(commandAndOptions_RuntimeExec);
        
      } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
      }
    }
}

RuntimeExecCaller.java

package conversion;

import java.io.IOException;

public class RuntimeExecCaller {
    Process process;
    
    // use one string
    public void methodA(String oneLongString) throws IOException {
      process = Runtime.getRuntime().exec(oneLongString);
    }
    
    // use the array
    public void methodB(String[] commandAndOptions) throws IOException {
      process = Runtime.getRuntime().exec(commandAndOptions);
    }
}

ProcessBuilderCaller.java

package conversion;

import java.io.IOException;

public class ProcessBuilderCaller {
    Process process;
    
    // use one string
    public void methodA(String oneLongString) throws IOException {
      process = new ProcessBuilder(oneLongString).start();
    }
    
    // use the array
    public void methodB(String[] commandAndOptions) throws IOException {
      process = new ProcessBuilder(commandAndOptions).start();
    }
}

Результат

Оба вызова methodA(String) работают, но когда вызывается methodB(String[]), Inkscape запускается и аргументы передаются неправильно. После выполнения methodB(String[]) я получаю диалоговое окно с ошибкой Inkscape для каждого высказывания

Не удалось загрузить запрошенный файл -f C:/test.svg -D -w 100 -h 100 -e C:\RuntimeExec-methodB.png

Не удалось загрузить запрошенный файл -f C:/test.svg -D -w 100 -h 100 -e C:\ProcessBuilder-methodB.png

и когда я нажимаю «Закрыть» в диалоговом окне, Inkscape появляется с новым пустым документом. Итак, я думаю, у меня есть несколько вопросов:

В чем разница между Runtime.getRuntime().exec(String) и Runtime.getRuntime().exec(String[])?

JavaDoc говорит, что Runtime.exec(String) вызывает Runtime.exec(command, null) (то есть Runtime. exec(String cmd, String[] envp)), который, в свою очередь, вызывает Runtime.exec(cmdarray, envp) (то есть Runtime.exec (String[] cmdarray, String[] envp)). Итак, если Runtime.getRuntime().exec(String) все равно вызывает Runtime.exec(String[]), почему я получаю разные результаты при использовании разных методов?

Что-то происходит за кулисами, когда Java настраивает среду по-разному в зависимости от того, какой метод вызывается?


person blastthisinferno    schedule 22.10.2010    source источник


Ответы (1)


Я подозреваю, что ваша проблема связана с тем, как вы указываете свой список аргументов. По сути, вы передаете Inkscape «-f C:/test.svg -D -w 100 -h 100 -e C:\RuntimeExec-methodB.png» как один единственный аргумент.

Что вам нужно сделать, так это передать аргументы отдельно, например:

String[] commandAndOptions_ProcessBuilder = {pathToInkscape, "-f", "C:\\est.svg", "-D", "-w", "100", "-h", "100", "-e", "C:\\ProcessBuilder-methodB.png"};
String[] commandAndOptions_RuntimeExec = {pathToInkscape, "-f", "C:\\test.svg", "-D", "-w", "100", "-h", "100", "-e","C:\\RuntimeExec-methodB.png"};

Грубо говоря, когда вы используете Runtime.exec(String), значение, которое вы передаете, оценивается оболочкой, которая анализирует список аргументов. Когда вы используете Runtime.exec(String[]), вы предоставляете список аргументов, поэтому его не нужно обрабатывать. Преимущество этого заключается в том, что вам не нужно экранировать специальные значения для оболочки, так как аргументы не будут оцениваться ею.

person userkci    schedule 22.10.2010
comment
Я почти уверен, что Java обрабатывает разделение аргументов либо на ProcessBuilder, либо на Runtime.exec(), но в остальном правильно. - person Jonathan; 22.10.2010
comment
@Jonathan, быстрый взгляд на Runtime показывает, что вы правы - StringTokenizer используется для разбиения строки на пробелы. Это означает, что если у вас есть пробелы в пути к исполняемому файлу, вам нужно будет использовать Runtime.exec(String[]). - person userkci; 22.10.2010