Java.io.IOException: 无法运行程序“...”:java.io.IOException: 错误=2,没有那个文件或目录。

10

我需要从Java中执行一个外部程序(偶然需要使用LibreOffice将一个文件转换为文件),我知道我需要的确切命令行:

/usr/bin/libreoffice --headless --convert-to pdf:'writer_pdf_Export' --outdir /home/develop/tomcat/mf/ROOT/private/docs/0/ /home/develop/tomcat/mf/ROOT/private/docs/0/35_invoice.fodt

从命令行执行时这个很完美。但是使用 ProcessBuilder 在 Java 中无法正常工作:

java.io.IOException: Cannot run program "/usr/bin/libreoffice --headless --convert-to pdf:'writer_pdf_Export' --outdir /home/develop/tomcat/mf/ROOT/private/docs/0 /home/develop/tomcat/mf/ROOT/private/docs/0/35_invoice.fodt": java.io.IOException: error=2, No such file or directory

我尝试了一些不同的方法但没有成功。这是最后一次测试的样本。

        List<String> command = new ArrayList<String>();
        command.add("/usr/bin/libreoffice");
        command.add("--headless");
        command.add("--convert-to pdf:'writer_pdf_Export' --outdir " + getDestinationDirectory(order) + " " + getInvoiceFilename() + ".fodt");
  
        ProcessBuilder builder = new ProcessBuilder(command);

        Process process = null;
        try {
            process = builder.start();
        } catch (IOException ex) {
            Logger.getLogger(Documents.class.getName()).log(Level.SEVERE, null, ex);
        }
        InputStream is = process.getInputStream();
        InputStreamReader isr = new InputStreamReader(is);
        BufferedReader br = new BufferedReader(isr);
        String line;
        try {
            while ((line = br.readLine()) != null) {
                System.out.println(line);
            }
        } catch (IOException ex) {
            Logger.getLogger(Documents.class.getName()).log(Level.SEVERE, null, ex);
        }
        System.out.println("Program terminated!");

1
当您尝试移动或执行某些操作的文件或目录不存在时,通常会出现此错误。您的命令可能是正确的,但是您的文件或路径没有到达另一侧(例如路径)。 - Vinicius Lima
@ViniciusLima 这不是问题的原因。 - Raedwald
4个回答

6

ProcessBuilder 构造函数要求外部程序的每个参数都必须是单独的(以数组或 List of String 的形式)。您所得到的第一个异常消息,

Cannot run program "/usr/bin/libreoffice --headless --convert-to pdf:'writer_pdf_Export' --outdir /home/develop/tomcat/mf/ROOT/private/docs/0 /home/develop/tomcat/mf/ROOT/private/docs/0/35_invoice.fodt"

您需要注意的是,不是因为找不到一个叫做/usr/bin/libreoffice的程序而抱怨。而是由于您将参数拼接成了一个非常长且特别的名称"/usr/bin/libreoffice --headless --convert-to pdf: 'writer_pdf_Export' --outdir /home/develop/tomcat/mf/ROOT/private/docs/0 /home/develop/tomcat/mf/ROOT/private/docs/0/35_invoice.fodt",导致无法找到该程序。

您应该将参数分开传递,而不是将它们拼接成一个String

例如:

command.add("--convert-to pdf:'writer_pdf_Export' --outdir " + getDestinationDirectory(order) + " " + getInvoiceFilename() + ".fodt")

以此类推,将每个参数拆分成自己的 List.add 调用。

command.add("--convert-to");
command.add("pdf:writer_pdf_Export");

command.add("--outdir");
command.add(getDestinationDirectory(order).toString());

command.add(getInvoiceFilename() + ".fodt");

请注意,“writer_pdf_Export”周围没有撇号,因为这些是shell元字符,在构建数组以传递给exec时不需要中介外壳。

我的系统上没有任何 filters/writer_pdf_Export.xcu,但如果我直接在 shell 中输入该命令,它就可以工作。 - Azathoth
拆分成两个部分(**command.add("--convert-to"); command.add("pdf:writer_pdf_Export");**)会导致 Unknown option: --outdir - Azathoth
@Azathoth,听起来我们正在取得进展,因为它抱怨了一个较晚的参数。你是否尝试按照我上面建议的将每个参数拆分成自己的“add”?也许更新您的问题并附上当前代码会使诊断更容易。 - Mike Samuel

1
尝试这个(保持简单)...
Process p = Runtime.getRuntime().exec("/usr/bin/libreoffice --headless --convert-to pdf:'writer_pdf_Export' --outdir "+ getDestinationDirectory(order)+" "+getInvoiceFilename()+".fodt");

完全...

    Process process = null;
    try {
            process = Runtime.getRuntime().exec("/usr/bin/libreoffice --headless --convert-to pdf:'writer_pdf_Export' --outdir "+ getDestinationDirectory(order)+" "+getInvoiceFilename()+".fodt");
    } catch (IOException ex) {
        Logger.getLogger(Documents.class.getName()).log(Level.SEVERE, null, ex);
    }
    BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream()));
    String line;
    try {
        while ((line = br.readLine()) != null) {
            System.out.println(line);
        }
    } catch (IOException ex) {
        Logger.getLogger(Documents.class.getName()).log(Level.SEVERE, null, ex);
    }
    br.close();
    System.out.println("Program terminated!");

这是其中一个最初的测试:没有错误,没有输出,最重要的是...没有PDF - Azathoth
当你读取进程的 InputStream 和进程的 ErrorStream 时,是否会出现相同的错误?请参阅 getInputStreamgetErrorStream - xagyg
正如我所说,如果我这样运行,什么都看不到:没有错误,也没有PDF。 - Azathoth
有些东西重叠了!现在,没有引号文件出现了!但是...不敢相信。它适用于发票,但是相同的命令对于订单确认无效!!谢谢(大家)! - Azathoth
@Azathoth,同意,如果客户对getDestinationDirectory(order)getInvoiceFilename()没有任何控制权,那么你就不用担心Shell注入的问题。但是,如果他们现在或将来可能对这些命令子字符串有控制权,并且任何命令子字符串都可以达到Shell,则存在风险。 - Mike Samuel
显示剩余12条评论

1
我尝试了这个线程中提出的每个解决方案,但都没有起作用。
在我的应用程序(使用TOMCAT在Linux上的Java Web应用程序)中,只有创建一个shell脚本并执行该脚本才能使其正常工作。但是,您必须在脚本中放置绝对路径,否则它将无法正常工作($ HOME无法正常工作)。此外,您可以传递参数。
例如:
Runtime.getRuntime().exec("/home/user/myscript.sh param1");

0
ProcessBuilder有时在处理长命令(具有大量参数的可执行路径)时可能会出现问题。 你可以这样做,
假设你的命令字符串为 String cmdString = "executableFilePath -param1 -param2"
将其转换为地图
List cmdMap = Arrays.asList(cmdString.split(" "));
将cmdMap传递给ProcessBuilder。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接