對應 30天挑戰精通 PowerShell 該書第 19 章。
本章中,我們將深入探討 PowerShell 的指令碼編寫( scripting ),但我們不會要你寫程式。相反地,我們會把重點放在指令碼的作用,把它視為一種避免「不必要的重複輸入」的方式。
Reference - P.299
當我一年前初次接觸由 PowerShell 編寫的 .ps1 文件時,對於它的魅力毫無感受。甚至一開始,我會考慮將現有的指令碼全數重寫成 Python,因為我對 Python 更為熟悉。然而,隨著時間的推移,我逐漸體會到 PowerShell 的獨特優勢。尤其是當面對特定的任務時,我開始傾向於使用 PowerShell,特別是在 Windows 操作系統上的相關操作。
例如,IIS 站台的管理、定期的檔案清理及備份等任務,透過 PowerShell 的命令相對簡單直接。我發現,當某個需求迫使我去研究如何以 PowerShell 指令來執行某項操作時,研究完成並成功執行後,我只需將這些命令寫入 .ps1 文件中。這樣,當下次需要執行相同任務時,我只需點兩下該文件,便能快速完成工作。
這種效率的提升讓我體會到,PowerShell 在自動化和操作系統層面上的優勢不容忽視。與 Python 相比,PowerShell 更貼近 Windows OS 的原生管理功能,能夠更直接地操作系統層級的任務。而這也是我從最初抗拒,到逐漸喜愛並依賴 PowerShell 的過程。
透過學習 PowerShell,我了解到它並不是傳統的程式設計,而更像是一種批次檔案的延伸,這也是接下來我們將探討的內容。
PowerShell 指令碼的編寫與傳統的程式設計不同,更接近於編寫批次檔案(Batch Files)。這意味著我們不需要擁有深厚的程式設計知識即可開始編寫指令碼。
PowerShell 的指令碼通常是將多個命令組合在一起,以快速執行重複性的任務,而不涉及複雜的程式結構或物件導向概念。
假設我們需要每天備份某個資料夾的內容。我可以編寫一個簡單的指令碼,將多個 Copy-Item 命令組合起來,然後定時執行這個指令碼。
# backup.ps1
Copy-Item -Path "C:\Data" -Destination "D:\Backup\Data" -Recurse
Copy-Item -Path "C:\Projects" -Destination "D:\Backup\Projects" -Recurse
這樣的指令碼並不涉及複雜的程式邏輯,而是簡單地執行一系列命令。
將命令放入指令碼中,可以使我們輕鬆地重複執行相同的任務,而不必每次都手動輸入完整的命令序列。除了提高效率,也降低了輸入錯誤的風險。
書中提供了該範例,其目的是從本機(localhost)取得邏輯磁碟的相關資訊,並進行格式化輸出。它主要透過 Get-CimInstance 指令來檢索磁碟資料,然後使用管線 (|) 來依序處理和格式化結果。
Get-CimInstance -class Win32_LogicalDisk -computername localhost `
-Filter "DriveType=3" | Sort-Object -Property DeviceID |
Format-Table -Property DeviceID ,
@{Label='FreeSpace(MB)'; expression={$_.FreeSpace / 1MB -as [int]}},
@{Label='Size(GB)'; expression={$_.Size / 1GB -as [int]}},
@{Label='%Free'; expression={$_.FreeSpace / $_.Size * 100 -as [int]}}
並在書中解釋了三個特殊符號在編寫 PowerShell 時的重要性:
是一個換行符(line continuation character),用來告訴 PowerShell 當前行尚未結束,下一行是這行命令的繼續。這在編寫長命令時特別有用,能讓指令碼更加可讀,避免單行命令過於冗長。
Get-CimInstance -class Win32_LogicalDisk -computername localhost `
-Filter "DriveType=3" | Sort-Object -Property DeviceID |
Get-CimInstance -class Win32_LogicalDisk -computername localhost -Filter "DriveType=3"
將一個命令的結果傳遞給下一個命令處理,是命令之間的串接符號。這是 PowerShell 中進行一連串操作的核心機制。這部分在前幾天已有詳盡解釋,這邊就不重新贅述。
主要在需要列出多個參數或項目時使用。逗號僅僅是用來在命令內部分隔多個屬性或值。
假設我們需要多次執行以下命令來清理系統日誌:
Clear-EventLog -LogName "Application","System","Security"
Get-ChildItem "C:\Temp" | Remove-Item -Force
便可以將這些命令保存為一個指令碼 clear\_log.ps1
,之後只需執行這個指令碼即可完成清理工作。
書中提到,通過在命令中使用變數,可以讓指令碼更具靈活性和可重用性。這樣,我們只需改變其變數的值,即可在不同的情況下使用相同的命令,而無需每次修改整段指令碼。
例如,將剛剛的範例( 19.2 範例一 ),變數 $computername
用來代替之前的硬編碼值 localhost
,使得命令更具靈活性:
# 將原先的值 'localhost' 存入變數
$computername = 'localhost'
# 使用變數 $computername 作為參數
Get-CimInstance -class Win32_LogicalDisk -computername $computername `
-Filter "DriveType=3" | Sort-Object -Property DeviceID |
Format-Table -Property DeviceID ,
@{Label='FreeSpace(MB)'; expression={$_.FreeSpace / 1MB -as [int]}},
@{Label='Size(GB)'; expression={$_.Size / 1GB -as [int]}},
@{Label='%Free'; expression={$_.FreeSpace / $_.Size * 100 -as [int]}}
雖然這種做法可以提高靈活性,但仍然需要手動更改變數 $computername
的值來適應不同情況。為了進一步提高指令碼的重用性,我們可以將 $computername
轉變成一個輸入參數,這樣在執行指令碼時,便可以靈活地指定不同的值,而無需修改代碼本身。
如下所示,通過 param 區塊來定義 $computername
變數,並設置預設值 server01
:
param (
$computername = 'server01'
)
Get-CimInstance -class Win32_LogicalDisk -computername $computername `
-Filter "DriveType=3" | Sort-Object -Property DeviceID |
Format-Table -Property DeviceID ,
@{Label='FreeSpace(MB)'; expression={$_.FreeSpace / 1MB -as [int]}},
@{Label='Size(GB)'; expression={$_.Size / 1GB -as [int]}},
@{Label='%Free'; expression={$_.FreeSpace / $_.Size * 100 -as [int]}}
如果直接執行這個指令碼而不指定任何參數,則 $computername
會自動使用預設值 server01
。
.\script.ps1
如果我們需要針對不同的電腦名稱( 如 localhost
)運行這段指令碼,則可以在執行時提供具體的值。指令碼將自動使用你指定的參數值去取代預設值。
.\script.ps1 -computername 'localhost'
這種做法的好處在於,當變數的值需要變動時,我們不再需要修改指令碼的內容,而是透過執行時的參數傳遞來控制,從而達到動態和靈活的效果。這樣,大家就能輕鬆應對不同的環境要求,而不會因為手動修改代碼而增加出錯的風險。
Day 23 - 指令碼編寫 Part 2