這是前陣子寫 PowerShell 我常踩到的小雷,大多發生在手動下指令的測試階段。

例如,在 PowerShell 裡我用 Set-Location (或使用 cd 指令別名) 將所在目錄切換成 X 資料夾,後續用 Out-File 寫入 ".\Test.json",Get-Content/ConvertFrom-Json 都能正確讀寫 X 資料夾下的 Test.json,但呼叫 [System.IO.File]::ReadAllText(".\Test.json") 卻找不到檔案。漸漸我才明白,.NET API 所認知的目前工作目錄與 PowerShell 不同,在 PowerShell.exe 這個 Process 啟動時決定,不會因 Set-Location 一起改變。

用個範例展示。開啟 PowerShell 視窗的工作目錄預設為 C:\Users\Jeffrey,接著 CD 切到 D:\Batch\TheJob,PowerShell Get-Location 查到的路徑是 D:\Batch\TheJob,但 [System.IO.Directory]::GetCurrentDirectory() 傳回的工作目錄仍是 C:\Users\Jeffrey:

若指令是寫成 .ps1 交由 PowerShell 執行,則 PowerShell 預設的 Location 跟工作目錄一開始是相同的,在 CD 或 Set-Location 後,二者也會開始不一致:

要解決不一致的問題,我常用的解法有兩種:

  1. 用 "$(PWD)\Test.txt" 轉為絕對路徑 (PWD 是 Get-Location 的別名)
  2. 呼叫 [System.IO.Directory]::SetCurrentDirectory($(PWD)) 將 .NET 工作目錄改成 PowerShell 當下的工作目錄

我偏好前者,理由是加 $(PWD) 只多打幾個字元,卻能讓檔案路徑在錯誤訊息或 Log 中資訊更清楚。另外順道一提,要取得 .ps1 所在目錄,可善用 $PSScriptRoot 內建變數,方便取得 .ps1 同目錄下的檔案。

以上經驗提供大家參考。

Explanation of the difference between PowerShell's current location and working directory for .NET.


Comments

# by Lik

黑大,請問你這個類似cmd指令editor是什麼工具?

# by Jeffrey

to Lik, Cmder! https://blog.miniasp.com/post/2015/09/27/Useful-tool-Cmder

Post a comment