2016-04-27 131 views
4

我使用類似於下面幾行的代碼。如果目錄被阻止,重命名可能會失敗,因爲某些應用程序中有一個文件已打開。檢測重命名失敗的原因

err := os.Rename("C:/temp/inUse", "c:/temp/Renamed") 

if err != nil { 
    fmt.Println(err) 
    ... 
} 

我能當我檢查err這個內容來檢測這樣一個事實:

重命名C:/溫度/ INUSE C:/溫度/重命名:明鏡Prozess卡恩nicht奧夫死 Datei zugreifen,da sie von einem anderen Prozess verwendet wird。

(這相當於「的方法是無法訪問該文件,因爲它是用來通過不同的處理」) 但是該消息取決於OS語言不同而不同。

是否有可能通過唯一的「錯誤代碼」檢測問題?我不介意該解決方案是否適用於Windows,並且不適用於Go支持的其他操作系統。

理想我可以做類似if err.Err == 32 { fmt.Println("Your directory is in use please ...

+1

我懷疑你可以從Go中的'err'中獲得更多細節。但是,如果遇到這個問題,我會使用像perfmon這樣的工具來找出什麼是鎖定文件,然後嘗試在我的應用程序代碼之外解決它 - 或者可能是代碼更改 - 但是如果兩個單獨的應用程序共享文件您必須使用外部程序來協調它們,例如編寫鎖定文件。我粗略瀏覽了Go的'os'包,告訴我沒有什麼可以直接給你這個信息。 – evanmcdonnal

+0

@evanmcdonnal感謝您的輸入。我知道如何使用sysinternals來找出哪個進程導致問題。我的目標是檢測原因並向用戶提示可能導致問題的提示 – Marged

+1

如果您有一些可以從命令行運行的東西,那麼您可以使用Go的執行庫(https://golang.org/pkg/os/ exec /)從您的應用程序中調用它,從std.out讀取輸出,解析鎖定文件的其他應用程序的列表,然後將其顯示給用戶。 – evanmcdonnal

回答

3

通過os.Rename返回的錯誤是*os.LinkError類型,讓你從操作系統訪問底層錯誤的。你應該能夠使用它來區分你遇到的特定錯誤。

您需要首先將錯誤轉換爲* os.LinkError。

例如,試圖將一個文件夾(/Users/t/pprof)到另一個名稱是寫保護(/Users/t/pprof2)重命名:

func TestRename(t *testing.T) { 
    err := os.Rename("/Users/t/pprof", "/Users/t/pprof2") 
    if err != nil { 
     e := err.(*os.LinkError) 
     t.Logf("Op: ", e.Op) 
     t.Logf("Old: ", e.Old) 
     t.Logf("New: ", e.New) 
     t.Logf("Err: ", e.Err) 
    } 
} 

提供以下的輸出:

Op: %!(EXTRA string=rename) 
Old: %!(EXTRA string=/Users/t/pprof) 
New: %!(EXTRA string=/Users/t/pprof2) 
Err: %!(EXTRA syscall.Errno=operation not permitted) 

的操作系統錯誤代碼可作爲Err成員訪問,但取決於您正在運行的操作系統會有所不同。

Err成員的類型爲syscall.Errno。爲了進一步檢查實際的錯誤,你需要把它轉換成該類型第一:

oserr := e.Err.(syscall.Errno) 

現在oserr可以在syscall包申報Errno的值進行比較。如果您在該頁面上搜索ENOENT,則會找到它們。

例如,你可以通過做檢查您的特定錯誤:

switch oserr { 
    case syscall.ENOENT: 
     // Handle this error 
    default: 
     // Handle other errors 
    } 
} 

一般情況下,這是非常方便的調試這些問題的排序時使用fmt.Printf。在上面的例子:

fmt.Println(err) 

打印

rename /Users/t/pprof /Users/t/pprof2: operation not permitted 

其中,作爲

fmt.Printf("%#v\n", err) 

打印

&os.LinkError{Op:"rename", Old:"/Users/t/pprof", New:"/Users/t/pprof2", Err:0x1} 

實際誤差透露更多細節,而不僅僅是它的字符串表示。

+0

謝謝,這些是我正在尋找的內幕。不幸的是,我不明白'testing.T'的來源。我怎麼稱呼這個? – Marged

+0

tests.T變量是go測試框架的一部分。這對編寫像這樣的測試用例的快速單元測試非常有用。您可以刪除方法簽名,並使用fmt.Printf替換t.Logf,以獲得沒有測試框架的類似功能。 –

+0

謝謝,但我只設法從'Err'中檢索文本(這是特定於OS語言環境的文本),但未能獲取基礎值。在我的場景中(我已經用你的例子測試過),我檢索&os.LinkError {操作:「重命名」,舊:「C:/ temp/inUse」,新建:「c:/ temp/Renamed」,Err:0x20} '但是當我訪問'Err'時,我得到'0x20'的翻譯結果。但我需要_raw_值。你有沒有想法如何檢索'0x20'而不需要在整個字符串上運行正則表達式? – Marged