如何检查文件是否被其他进程打开(Java/Linux)?

32

我正在尝试检查某个 java.io.File 是否被外部程序打开。在Windows上,我使用这个简单的技巧:

try {
    FileOutputStream fos = new FileOutputStream(file);
    // -> file was closed
} catch(IOException e) {
    // -> file still open
}

我知道Unix系统可以在多个进程中打开文件......是否有类似的技巧可以在Unix系统上实现相同的结果?

非常感谢任何帮助/技巧 :-)


你说得没错... 但我需要这个功能来监控文件状态(我使用Java 7中的WatchServices实现)。但我还需要检测特定文件何时再次关闭以解锁它,以便其他用户可以再次编辑它。 - salocinx
这是我接下来要尝试的事情。lsof似乎存在于许多Linux发行版中。使用lsof打开一个新进程并读取标准输出将完成任务。明天我会在这个帖子中展示我的解决方案。到目前为止,非常感谢! - salocinx
我觉得目前似乎还没有可移植的解决方案。 - Edward
5个回答

13

以下是如何在基于Unix的系统中使用lsof的示例:

public static boolean isFileClosed(File file) {
    try {
        Process plsof = new ProcessBuilder(new String[]{"lsof", "|", "grep", file.getAbsolutePath()}).start();
        BufferedReader reader = new BufferedReader(new InputStreamReader(plsof.getInputStream()));
        String line;
        while((line=reader.readLine())!=null) {
            if(line.contains(file.getAbsolutePath())) {                            
                reader.close();
                plsof.destroy();
                return false;
            }
        }
    } catch(Exception ex) {
        // TODO: handle exception ...
    }
    reader.close();
    plsof.destroy();
    return true;
}
希望这能帮到你。

5

这个方法也适用于Windows系统。但是需要注意,它不适用于Linux!

     private boolean isFileClosed(File file) {  
            boolean closed;
            Channel channel = null;
            try {
                channel = new RandomAccessFile(file, "rw").getChannel();
                closed = true;
            } catch(Exception ex) {
                closed = false;
            } finally {
                if(channel!=null) {
                    try {
                        channel.close();
                    } catch (IOException ex) {
                        // exception handling
                    }
                }
            }
            return closed;
    }

3
感谢原始建议。我有一个小的升级,对于那种方法来说有些重要:
FileOutputStream fos = null;
try {
    // Make sure that the output stream is in Append mode. Otherwise you will
    // truncate your file, which probably isn't what you want to do :-) 
    fos = new FileOutputStream(file, true);
    // -> file was closed
} catch(IOException e) {
    // -> file still open
} finally {
    if(fos != null) {
    try {
        fos.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

欢呼, Gumbatron

注意:不支持Linux系统。即使文件正在使用中,您仍然可以打开它。 - mdewit

3
你可以通过Java程序运行Unix实用程序 lsof,以查看哪个进程正在使用文件,然后分析其输出。要从Java代码中运行程序,请使用例如RuntimeProcessProcessBuilder类。请注意:在这种情况下,您的Java程序将不具备可移植性,与可移植性概念相矛盾,因此请三思而后行 :)

非常感谢您的解决方案。我知道我正在涉及可移植性问题,但目标平台仅限于win/unix/mac。希望我能为每个操作系统找到一个“稳定”的策略 :-) - salocinx
6
更大的问题不在于可移植性,而是即使你使用 lsof 并分析其输出,发现没有人在使用你感兴趣的文件,也不能保证在你代码的下一行中,没有打开该文件的程序。 - Vladimir Tsvetkov

0
您可以尝试使用@ZZ Coder的信号量类型代码来进行文件锁定。
File file = new File(fileName);
FileChannel channel = new RandomAccessFile(file, "rw").getChannel();

FileLock lock = channel.lock();
try {
    lock = channel.tryLock();
    // Ok. You get the lock
} catch (OverlappingFileLockException e) {
    // File is open by someone else
  } finally {
   lock.release();
}

3
只有每个人都使用这个代码才能起作用。这一点根本不现实。 - tchrist
3
无法在Ubuntu/Linux上运行。即使文件已在另一个进程中打开,文件系统也会让我获取锁定。但在Windows上可以正常工作。有没有任何思路如何在Linux上实现相同的结果? - salocinx

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