![](/img/trans.png)
[英]How to detect when two linux paths refer to the same file in maven?
[英]Java, Linux: how to detect whether two java.io.Files refer to the same physical file
我正在尋找一種有效的方法來檢測兩個java.io.File
是否指的是同一個物理文件。 根據文檔, File.equals()
應該完成這項工作:
測試這個抽象路徑名是否與給定的 object 相等。 當且僅當參數不是 null 並且是表示與此抽象路徑名相同的文件或目錄的抽象路徑名時才返回 true。
然而,給定一個掛載在 /media/truecrypt1 的 FAT32 分區(實際上是一個 TrueCrypt 容器):
new File("/media/truecrypt1/File").equals(new File("/media/truecrypt1/file")) == false
你會說這符合規范嗎? 在這種情況下,如何解決這個問題?
更新:感謝評論者,對於 Java 7 我發現java.io.Files.isSameFile()
對我有用。
@Joachim 評論中的答案通常是正確的。 判斷兩個File
object 是否引用同一個 OS 文件的方法是使用 getCanonicalFile() 或 getCanonicalPath()。 javadoc是這樣說的:
“規范路徑名是絕對且唯一的。[...] 表示現有文件或目錄的每個路徑名都有唯一的規范形式。”
所以以下應該工作:
File f1 = new File("/media/truecrypt1/File"); // different capitalization ...
File f2 = new File("/media/truecrypt1/file"); // ... but same OS file (on Windows)
if (f1.getCanonicalPath().equals(f2.getCanonicalPath())) {
System.out.println("Files are equal ... no kittens need to die.");
}
但是,您似乎正在查看安裝在 UNIX / Linux 上的 FAT32 文件系統。 AFAIK,Java 不知道這種情況正在發生,並且只是應用通用 UNIX / Linux 文件名在此場景規則中給出了錯誤的答案...
如果這是真的發生的事情,我認為純 Java 6 中沒有可靠的解決方案。但是,
你可以做一些毛茸茸的 JNI 東西; 例如,獲取文件描述符編號,然后在本機代碼中,使用fstat(2)
系統調用來獲取兩個文件的設備和 inode 編號並進行比較。
Java 7 java.nio.file.Path.equals(Object)
看起來如果您首先在路徑上調用resolve()
來解析符號鏈接,它可能會給出正確的答案。 (從 javadoc 中不清楚 Linux 上的每個已安裝文件系統是否將對應於不同的文件系統FileSystem
。)
Java 7 教程有本節介紹查看兩個Path
對象是否用於同一個文件...建議使用java.nio.file.Files.isSameFile(Path, Path)
你會說這符合規范嗎?
不,是的。
沒有,因為getCanonicalPath()
方法沒有為每個現有的 OS 文件返回相同的值……這是您閱讀 javadoc 所期望的。
是的,從技術意義上講,Java 代碼庫(不是 javadoc)是最終規范......無論是在理論上還是在實踐中。
您可以嘗試獲取文件的獨占寫鎖,看看是否失敗:
boolean isSame;
try {
FileOutputStream file1 = new FileOutputStream (file1);
FileOutputStream file2 = new FileOutputStream (file2);
FileChannel channel1 = file1.getChannel();
FileChannel channel2 = file2.getChannel();
FileLock fileLock1 = channel1.tryLock();
FileLock fileLock2 = channel2.tryLock();
isSame = fileLock2 != null;
} catch(/*appropriate exceptions*/) {
isSame = false;
} finally {
fileLock1.unlock();
fileLock2.unlock();
file1.close();
file2.close();
///cleanup etc...
}
System.out.println(file1 + " and " + file2 + " are " + (isSame?"":"not") + " the same");
這並不總是保證是正確的 - 因為另一個進程可能已經獲得了鎖,因此對你來說失敗了。 但這至少不需要您將 shell 輸出到外部進程。
同一個文件有可能有兩個路徑(例如,通過網絡\\localhost\file
和\\127.0.0.1\file
將引用具有不同路徑的同一個文件)。 我會 go 比較兩個文件的摘要以確定它們是否相同。 像這樣的東西
public static void main(String args[]) {
try {
File f1 = new File("\\\\79.129.94.116\\share\\bots\\triplon_bots.jar");
File f2 = new File("\\\\triplon\\share\\bots\\triplon_bots.jar");
System.out.println(f1.getCanonicalPath().equals(f2.getCanonicalPath()));
System.out.println(computeDigestOfFile(f1).equals(computeDigestOfFile(f2)));
}
catch(Exception e) {
e.printStackTrace();
}
}
private static String computeDigestOfFile(File f) throws Exception {
MessageDigest md = MessageDigest.getInstance("MD5");
InputStream is = new FileInputStream(f);
try {
is = new DigestInputStream(is, md);
byte[] buffer = new byte[1024];
while(is.read(buffer) != -1) {
md.update(buffer);
}
}
finally {
is.close();
}
return new BigInteger(1,md.digest()).toString(16);
}
它輸出
false
true
這種方法當然比任何類型的路徑比較都要慢得多,它還取決於文件的大小。 另一個可能的副作用是兩個文件將被視為相等,與它們的位置無關。
Files.isSameFile 方法正是為這種用法而添加的——也就是說,您要檢查兩個不相等的路徑是否定位到同一個文件。
在 *nix 系統上,大小寫確實很重要。 file
與File
或fiLe
。
equals()
的 API 文檔說(在您的報價之后):
在 UNIX 系統上,字母大小寫在比較路徑名時很重要; 在 Microsoft Windows 系統上它不是。
你可以試試 Runtime.exec() 的
ls -i /fullpath/File # extract the inode number.
df /fullpath/File # extract the "Mounted on" field.
如果掛載點和“inode”編號相同,則無論您有符號鏈接還是不區分大小寫的文件系統,它們都是同一個文件。
甚至
bash test "file1" -ef "file2"
FILE1 和 FILE2 具有相同的設備和 inode 號
測試兩個文件名是否引用相同的底層文件系統 object 的傳統 Unix 方法是stat
它們並測試它們是否具有相同的[dev,ino]
對。
但是,這確實假設沒有多余的安裝。 如果這些是允許的,你必須以不同的方式對它進行 go 。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.