下面是一個調用java的文件重命名方法的小程序:
- import java.io.*;
- public class FileRenameTest
- {
- public static void main(String[] args)
- {
- if(args.length<2)
- {
- System.out.println("Usage:java FileRenameTest <srcFileName> <targetFileName>");
- return ;
- }
- System.out.println("prepare to renaming file["+args[0]+"] to file["+args[1]+"]");
- boolean status= new File(args[0]).renameTo(new File(args[1]));
- System.out.println("status:"+String.valueOf(status));
- }
- }
在aix5.1或rhel6.1上準備把一個文件重命名到另一個文件系統中。最終執行的結果都是不能成功。
爲什麼通過系統命令mv卻可以成功把一個文件重命名到另一個設備中呢?
在aix通過下列命令來觀察此java程序執行過程系統內部的處理過程。
- truss -f -o rename.aix.truss java -cp . FileRenameTest origin.txt /tmp/origin.txt
在輸出文件中找到下列重要信息。
- 8486994: 5308421: kwrite(1, " p r e p a r e t o r".., 61) = 61
- 8486994: 5308421: kwrite(1, "\n", 1) = 1
- 8486994: 5308421: rename("origin.txt", "/tmp/origin.txt") Err#18 EXDEV
- 8486994: 5308421: kwrite(1, " s t a t u s : f a l s e", 12) = 12
- 8486994: 5308421: kwrite(1, "\n", 1) = 1
從上面的內容可以看系統調用rename執行的結果是失敗。Errno是18,也就是EXDEV錯誤。
在rhel6.1通過下列命令來觀察此java程序執行過程系統內部的處理過程。
- strace -fxo rename.rh.strace java -cp . FileRenameTest origin.txt /tmp/origin.txt
在輸出文件中可以找到下列重要信息。
- 19517 write(1, "prepare to renaming file[origin."..., 61) = 61
- 19517 write(1, "\n", 1) = 1
- 19517 rename("origin.txt", "/tmp/origin.txt") = -1 EXDEV (Invalid cross-device link)
- 19517 write(1, "status:false", 12) = 12
- 19517 write(1, "\n", 1) = 1
失敗的原因與aix上失敗的原因是一致的,都是非法的跨設備的操作。
我們來觀察一下在aix及rhel上mv命令的執行過程。
通過下列命令來觀察aix上mv的執行過程
- truss -f -o mv.aix.truss mv origin.txt /tmp/origin.txt
輸出文件中可以看到下列重要信息。
- 6283516: 10338403: statx("origin.txt", 0x2FF22570, 176, 021) = 0
- 6283516: 10338403: statx("/tmp/origin.txt", 0x2FF22620, 176, 021) Err#2 ENOENT
- 6283516: 10338403: access("/tmp/origin.txt", 0) Err#2 ENOENT
- 6283516: 10338403: rename("origin.txt", "/tmp/origin.txt") Err#18 EXDEV
- 6283516: 10338403: statx("origin.txt", 0x2FF22570, 176, 021) = 0
- 6283516: 10338403: open("origin.txt", O_RDONLY|O_LARGEFILE) = 3
- 6283516: 10338403: statx("/tmp/origin.txt", 0x2FF22198, 176, 020) Err#2 ENOENT
- 6283516: 10338403: open("/tmp/origin.txt", O_WRONLY|O_CREAT|O_TRUNC|O_LARGEFILE, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) = 4
- 6283516: 10338403: fstatx(3, 0x2FF22248, 176, 020) = 0
- 6283516: 10338403: fstatx(4, 0x2FF222F8, 176, 020) = 0
- 6283516: 10338403: fstatfs(4, 0x2FF22430) = 0
- 6283516: 10338403: kread(3, " w w w w w w w\n\0\0\0\0".., 4096) = 8
- 6283516: 10338403: kwrite(4, " w w w w w w w\n", 8) = 8
- 6283516: 10338403: kread(3, " w w w w w w w\n\0\0\0\0".., 4096) = 0
- 6283516: 10338403: statx("origin.txt", 0x2FF22038, 176, 020) = 0
- 6283516: 10338403: __statxacl("origin.txt", 0x0000000000000000, 0x00000000, 0x2FF22028, 0x30003D08, 0x2FF22010) = 0
- 6283516: 10338403: chown("/tmp/origin.txt", 212, 1) = 0
- 6283516: 10338403: chmod("/tmp/origin.txt", 0100644) = 0
- 6283516: 10338403: __chxacl("/tmp/origin.txt", 0x0000000000000000, 0x0000000000000002, 0x41495843, 0, 06000036410) = 0
- 6283516: 10338403: listea("origin.txt", "", 0) Err#48 EFORMAT
- 6283516: 10338403: close(3) = 0
- 6283516: 10338403: close(4) = 0
- 6283516: 10338403: utimes("/tmp/origin.txt", 0x2FF22188) = 0
- 6283516: 10338403: unlink("origin.txt") = 0
mv命令執行過程大致如下:先調用rename,如果不成功則讀原文件內容複製到新創建的目錄文件中。再通過unlink取消原文件的鏈接數。如果原文件鏈接數爲0,則會刪除此文件。
在rhel上mv內部調用過程總體上與aix上是一致的。
不知道是基於什麼原因考慮,java類庫中renameTo方法的內部執行不與mv的執行保持一致。我覺得這是一個API實現上的瑕疵。