寫 PowerShell Script 時,針對較複雜或步驟較多的作業,我會在執行過程加上 Write-Host 或 Write-Output 顯示一些資訊,一方面是回報進度可避免操作者茫然焦慮,另一方面,若發生錯誤,這些線索是判斷問題點的重要依據。

PowerShell 新手的疑問是: Write-Host 跟 Write-Output 都能顯示訊息,二者有什麼不同?

簡單說,Write-Host 只會顯示在 Console,Write-Output 則可串 Pipeline 導向給其他程序處理。用個簡單實驗說明,我寫了一個 test.ps1:

Write-Host "Displayed by Write-Host"
Write-Output "Displayed by Write-Output"

單獨執行 test.ps1 時,兩段文字都會顯示,沒什麼差異。用 test1.ps1 | Out-File .\testdump.txt 串 Pipeline 將輸出寫成檔案,則 Console 只顯示 Displayed by Write-Host、文字檔只有 Displayed by Write-Output。這樣,大家應該就懂二者的差異了。

Wirte-Output 能做到手動執行時顯示在 Console,跑排程時導向 Log 檔案,看起來是較好的做法。不過,有些應用情境我希望就算手動執行,Console 顯示內容也該同步寫入 Log 以掌握曾經執行的所有記錄。我找到一個好用的解決方案 - Write-Host + Start-Transcript。

Start-Transcript 指令可側錄 Console 輸出內容寫入指定檔案,它有幾個參數:

  • -Path
    指定 txt 檔路徑。若不沒指定路徑,PowerShell 會寫到 $Transcript 全域參數指向的目錄,預設為 $Home\My Documents,而檔名為 PowerShell_transcript.<time-stamp>.txt,例如:PowerShell_transcript.JEFFWIN10.ke8ee_nJ.20200530102955.txt
  • -OuptutDirectory
    只指定路徑,檔名為 PowerShell_transcript.<time-stamp>.txt
  • -Append
    若檔案已存在,附加內容而不要覆寫
  • -Force
    覆寫唯讀檔案
  • -NoClobber
    不覆寫唯讀檔案,若檔案存在會出錯

輸出結果像這樣,上下有 PowerShell 附加的執行時間、機器名稱、版本等資訊,中間黃色部分是 test.ps1 的輸出內容。

以下是打包 IIS Log 檔的實例:(延伸閱讀 壓縮並刪除一個月前的 Log 檔)

param (
    [Parameter(Mandatory = $True)]
    [string] $iisLogPath #Log目錄
)
$ErrorActionPreference = "STOP"
$psPath = [System.IO.Path]::GetDirectoryName($PSCommandPath)
$cmd = "$psPath\7z.exe";
Start-Transcript -Path "$psPath\Logs\$(Get-Date -Format 'yyyy-MM-dd').log" -Append
$reserveDate = [DateTime]::Today.AddDays( - [DateTime]::Today.Day + 1).AddMonths(-1).ToString("yyMMdd");
Get-ChildItem -Path $iisLogPath -Filter *.log | Where-Object { $_.Name -lt "u_ex$reserveDate" } |
ForEach-Object { $_.Name.Substring(0, 8) } | Sort-Object -Unique |
ForEach-Object { 
    $params = "a -mx9 `"$iisLogPath\$_.7z`" $_*.log".Split(" ")
    & $cmd $params 2>&1 | Write-Host
    Remove-Item -Path "$iisLogPath\$_*.log" 
}

簡單說明兩點:

  1. PowerShell_transcript.JEFFWIN10.ke8ee_nJ.20200530102955.txt 自動命名每次存一個檔太瑣碎,我習慣用 "$psPath\Logs$(Get-Date -Format 'yyyy-MM-dd').log" 依日、月或年分檔。
  2. 範例中呼叫了 7z.exe,外部程式的輸出原本是不會被記錄的,故要加上 2>&1 | Write-Host 整合進來。參考

如果想要選擇性收錄寫入檔案內容,有幾種做法:

  1. 用 Stop-Transcript 停止側錄,稍後再打開 (像是電影裡 FBI 探員問案到一半先關錄音機方便揍人)
  2. 不用 Start-Transcript,交替使用 Write-Host 跟 Write-Output
  3. 自己寫函式開啟並輸出 Log 檔

Example of using Start-Transccript to record the Write-Host output duration PowerShell script execution.


Comments

Be the first to post a comment

Post a comment