Java:无法删除ProcessBuilder使用的文件

pinkon5k  于 2023-05-21  发布在  Java
关注(0)|答案(2)|浏览(183)

我有这个代码:

try {
    // Create a temporary file
    Path tmpFilePath = Files.createTempFile("tmp-errors", ".log");

    // Create a ProcessBuilder and redirect the error stream to the temporary file
    ProcessBuilder pb = new ProcessBuilder("my-command")
            .redirectError(Redirect.appendTo(tmpFilePath.toFile()));

    // Start the process
    Process process = pb.start();

    // log the erros into this thread
    Files.readAllLines(tmpFilePath,StandardCharsets.ISO_8859_1).forEach(LOGGER::error);

    // Delete the temporary file after the process completes
    Files.delete(tmpFilePath);

} catch (IOException | InterruptedException e) {
    // Handle the exception
    e.printStackTrace();
}

我想使用Process Builder执行外部进程,收集来自外部进程的错误使用redirectError()方法,从临时文件中收集这些错误,并将它们记录在当前线程中,最后删除临时文件。
但我一直得到这个错误:

The process cannot access the file because it is being used by another process

我认为文件仍然被Process Builder锁定,但我找不到如何释放它!

pkbketx9

pkbketx91#

调用ProcessBuilder.start()方法。该方法返回一个Process示例,在it's documentation中,您可以看到

  • 默认情况下,创建的进程没有自己的终端或控制台。其所有标准I/O(即stdin、stdout、stderr)操作将被重定向到父进程,在父进程中,可以通过使用方法getOutputStream()、getInputStream()和getErrorStream()获得的流来访问这些操作。父进程使用这些流向进程提供输入并从进程获取输出。由于一些原生平台仅为标准输入和输出流提供有限的缓冲区大小,因此未能及时写入进程的输入流或读取进程的输出流可能会导致进程阻塞,甚至死锁。

因此,您的进程可能是由操作系统启动的,但由于I/O限制而被阻塞。通过阅读STDOUT和STDERR流来解决这个问题,直到您的进程完成。或者重定向流,如下所示:

ProcessBuilder pb = new ProcessBuilder("cmd.exe");
pb.redirectOutput(Redirect.appendTo(tmpFilePath.toFile()));
pb.redirectError(Redirect.appendTo(tmpFilePath.toFile()));
Process p = pb.start();
p.waitFor();
System.out.println("process exited with " + p.exitValue());

一旦进程终止,您应该能够正常访问或删除临时文件。

km0tfn4u

km0tfn4u2#

正如在注解和其他答案中提到的,您的子进程将写入临时文件,直到它退出,所以您必须在尝试删除之前调用waitFor()等待进程退出。
如果waitFor()没有返回或者子进程似乎冻结了,那么可能的后续问题是stdout缓冲区已满。为了确保正确的终止,在阅读+删除临时文件之前,添加stdout consumer来读取process.getInputStream()并等待进程:

// Create a temporary file
Path tmpFilePath = Files.createTempFile("tmp-errors", ".log");

// Create a ProcessBuilder and redirect the error stream to the temporary file
ProcessBuilder pb = new ProcessBuilder("my-command")
        .redirectError(Redirect.appendTo(tmpFilePath.toFile()));

// Start the process
Process process = pb.start();

// Consume STDOUT with another file, memory or:
process.getInputStream().transferTo(System.out);

// Wait for exit:
int rc = process.waitFor();

// log the erros into this thread
Files.readAllLines(tmpFilePath,StandardCharsets.ISO_8859_1).forEach(LOGGER::error);

// Delete the temporary file after the process completes
Files.delete(tmpFilePath);

明显冻结的另一个原因是当子进程使用控制台输入时。如果是这种情况,您还应在start()之后和阅读STDOUT之前发出输入结束信号:

try(OutputStream os = p.getOutputStream()) { 
    // empty
}

如果每次都创建临时文件,则可以删除追加模式的使用,将redirectError替换为.redirectError(tmpFilePath.toFile())

相关问题