當我開始學習 PowerShell 時,我一開始依賴圖形使用者介面(GUI)來處理對於簡單 PowerShell 命令而言似乎太複雜的工作。 不過,當我繼續學習時,我改善了我的技能,並已從基本單行程式移至建立腳本、函式和模組。 記住,因為網路上的一些高深範例而感到不知所措是正常的。 沒有人會以PowerShell專家身分開始;我們都從初學者開始。
對於主要針對系統管理工作使用 GUI 的人員,請在系統管理工作站上安裝管理工具,以遠端管理伺服器。 無論您的伺服器使用 GUI 或 Server Core OS 安裝,此方法都很有用。 若要熟悉遠端伺服器管理,以準備使用PowerShell執行系統管理工作,這是一個實用的方法。
如同上一章,請在實驗室環境中嘗試這些概念。
One-Liners
PowerShell 單行指令是一個連續管線。 常見的誤解是,實體行上的命令就是 PowerShell 的單行命令,但這並不一定正確。
例如,請考慮下列範例:命令延伸到多個實體行,但它是 PowerShell 單行命令,因為它構成了連續的管道。 建議在管道符號處進行換行,因為它是 PowerShell 中的一個自然斷點,以增強單行命令的可讀性和清晰性。 這種換行符的策略用途可改善可讀性,而不會中斷過程的流暢度。
Get-Service |
Where-Object CanPauseAndContinue -EQ $true |
Select-Object -Property *
Name : LanmanWorkstation
RequiredServices : {NSI, MRxSmb20, Bowser}
CanPauseAndContinue : True
CanShutdown : False
CanStop : True
DisplayName : Workstation
DependentServices : {SessionEnv, Netlogon}
MachineName : .
ServiceName : LanmanWorkstation
ServicesDependedOn : {NSI, MRxSmb20, Bowser}
ServiceHandle :
Status : Running
ServiceType : Win32OwnProcess, Win32ShareProcess
StartType : Automatic
Site :
Container :
Name : Netlogon
RequiredServices : {LanmanWorkstation}
CanPauseAndContinue : True
CanShutdown : False
CanStop : True
DisplayName : Netlogon
DependentServices : {}
MachineName : .
ServiceName : Netlogon
ServicesDependedOn : {LanmanWorkstation}
ServiceHandle :
Status : Running
ServiceType : Win32ShareProcess
StartType : Automatic
Site :
Container :
Name : vmicheartbeat
RequiredServices : {}
CanPauseAndContinue : True
CanShutdown : False
CanStop : True
DisplayName : Hyper-V Heartbeat Service
DependentServices : {}
MachineName : .
ServiceName : vmicheartbeat
ServicesDependedOn : {}
ServiceHandle :
Status : Running
ServiceType : Win32OwnProcess, Win32ShareProcess
StartType : Manual
Site :
Container :
Name : vmickvpexchange
RequiredServices : {}
CanPauseAndContinue : True
CanShutdown : False
CanStop : True
DisplayName : Hyper-V Data Exchange Service
DependentServices : {}
MachineName : .
ServiceName : vmickvpexchange
ServicesDependedOn : {}
ServiceHandle :
Status : Running
ServiceType : Win32OwnProcess, Win32ShareProcess
StartType : Manual
Site :
Container :
Name : vmicrdv
RequiredServices : {}
CanPauseAndContinue : True
CanShutdown : False
CanStop : True
DisplayName : Hyper-V Remote Desktop Virtualization Service
DependentServices : {}
MachineName : .
ServiceName : vmicrdv
ServicesDependedOn : {}
ServiceHandle :
Status : Running
ServiceType : Win32OwnProcess, Win32ShareProcess
StartType : Manual
Site :
Container :
Name : vmicshutdown
RequiredServices : {}
CanPauseAndContinue : True
CanShutdown : False
CanStop : True
DisplayName : Hyper-V Guest Shutdown Service
DependentServices : {}
MachineName : .
ServiceName : vmicshutdown
ServicesDependedOn : {}
ServiceHandle :
Status : Running
ServiceType : Win32OwnProcess, Win32ShareProcess
StartType : Manual
Site :
Container :
Name : vmicvss
RequiredServices : {}
CanPauseAndContinue : True
CanShutdown : False
CanStop : True
DisplayName : Hyper-V Volume Shadow Copy Requestor
DependentServices : {}
MachineName : .
ServiceName : vmicvss
ServicesDependedOn : {}
ServiceHandle :
Status : Running
ServiceType : Win32OwnProcess, Win32ShareProcess
StartType : Manual
Site :
Container :
Name : webthreatdefsvc
RequiredServices : {RpcSs, wtd}
CanPauseAndContinue : True
CanShutdown : True
CanStop : True
DisplayName : Web Threat Defense Service
DependentServices : {}
MachineName : .
ServiceName : webthreatdefsvc
ServicesDependedOn : {RpcSs, wtd}
ServiceHandle :
Status : Running
ServiceType : Win32OwnProcess, Win32ShareProcess
StartType : Manual
Site :
Container :
Name : webthreatdefusersvc_644de
RequiredServices : {}
CanPauseAndContinue : True
CanShutdown : True
CanStop : True
DisplayName : Web Threat Defense User Service_644de
DependentServices : {}
MachineName : .
ServiceName : webthreatdefusersvc_644de
ServicesDependedOn : {}
ServiceHandle :
Status : Running
ServiceType : 240
StartType : Automatic
Site :
Container :
Name : Winmgmt
RequiredServices : {RPCSS}
CanPauseAndContinue : True
CanShutdown : True
CanStop : True
DisplayName : Windows Management Instrumentation
DependentServices : {}
MachineName : .
ServiceName : Winmgmt
ServicesDependedOn : {RPCSS}
ServiceHandle :
Status : Running
ServiceType : Win32OwnProcess, Win32ShareProcess
StartType : Automatic
Site :
Container :
自然換行符可以發生在常用的字元,包括逗號(,)和左括弧([)、大括號({)以及小括號(()。 其他不太常見的包括分號(;)、等號(=),以及開啟單引號和雙引號(',")。
使用反引號(`)或嚴重強調字元作為行延續是有爭議的。 最好盡可能避免。 在自然換行符之後使用反引號是常見的錯誤。 此備援是不必要的,而且可能會雜亂程序代碼。
下列範例中的命令會從PowerShell控制台正確執行。 不過,嘗試在PowerShell整合式腳本環境 (ISE) 的控制台窗格中執行它們會導致錯誤。 發生此差異的原因是,不同於PowerShell控制台,ISE 的控制檯窗格不會自動預期命令接續到下一行。 若要避免此問題,請在 ISE 的控制檯窗格中按 Shift+Enter,而不是在您需要跨多行擴充命令時,Enter。 這個按鍵組合會向ISE發出訊號,指出命令會在下一行繼續執行,以避免執行導致錯誤。
Get-Service -Name w32time |
Select-Object -Property *
Name : w32time
RequiredServices : {}
CanPauseAndContinue : False
CanShutdown : True
CanStop : True
DisplayName : Windows Time
DependentServices : {}
MachineName : .
ServiceName : w32time
ServicesDependedOn : {}
ServiceHandle :
Status : Running
ServiceType : Win32OwnProcess, Win32ShareProcess
StartType : Manual
Site :
Container :
下一個範例不符合成為 PowerShell 單行命令的標準,因為它不是一個連續的管道。 相反地,這是放在單一行上的兩個獨立的命令,並以分號分隔。 這個分號表示一個命令的結尾和另一個命令的開頭。
$Service = 'w32time'; Get-Service -Name $Service
Status Name DisplayName
------ ---- -----------
Running w32time Windows Time
許多程式設計與腳本語言都需要每一行結尾的分號。 不過,在 PowerShell 中,行尾的分號是不必要的,不建議使用。 您應該避免使用它們,以使程序代碼更簡潔且更易於閱讀。
向左篩選
本章示範如何篩選各種命令的結果。
在 PowerShell 中,最好盡可能早地在管線中篩選結果。 達成此動作牽涉到在初始命令上使用參數來套用篩選,通常是在管線的開頭。 這通常稱為 篩選左。
為了說明這個概念,請考慮下列範例:使用 的 Get-Service 參數來篩選管線開頭的結果,只傳回 Windows Time 服務的詳細數據。 此方法示範有效率的數據擷取,確保您只傳回必要和相關信息。
Get-Service -Name w32time
Status Name DisplayName
------ ---- -----------
Running w32time Windows Time
常見的情況是,將 PowerShell 指令透過管道(pipeline)傳送至 Where-Object Cmdlet 來過濾其結果的在網路上的範例。 如果管線中先前的命令具有參數來執行篩選,則這項技術效率不佳。
Get-Service | Where-Object Name -EQ w32time
Status Name DisplayName
------ ---- -----------
Running W32Time Windows Time
第一個範例示範直接在來源進行篩選,並特別傳回 Windows Time 服務的結果。 相反地,第二個範例會擷取所有服務,然後使用另一個命令來篩選結果。 在小規模案例中,這似乎微不足道,但請考慮涉及大型數據集的情況,例如 Active Directory。 擷取數千個用戶帳戶的詳細數據效率不佳,只能將其縮小為小型子集。 實踐 左側篩選 — 在命令序列中儘可能早地套用篩選,即便在看似微不足道的情況下也要這樣做。 此習慣可確保在更複雜的案例中,效率會變得更重要。
有效篩選的命令排序
普遍存在一個誤解,即認為 PowerShell 中命令的順序無關緊要,但這是一種錯誤的理解。 您排列命令的順序,特別是在篩選時很重要。 例如,假設您使用 Select-Object 來選擇特定屬性,並 Where-Object 篩選。 在此情況下,必須先套用篩選。 無法這麼做表示管線中可能無法使用必要的屬性進行篩選,導致結果無效或錯誤。
下列範例無法產生結果,因為當 被傳送至 Select-Object時,Where-Object 屬性不存在。 這是因為 CanPauseAndContinue 屬性未包含在 Select-Object所做的選取範圍中。 實際上,它會被排除或篩選掉。
Get-Service |
Select-Object -Property DisplayName, Running, Status |
Where-Object CanPauseAndContinue
將 Select-Object 和 Where-Object 的順序對調,會產生所需的結果。
Get-Service |
Where-Object CanPauseAndContinue |
Select-Object -Property DisplayName, Status
DisplayName Status
----------- ------
Workstation Running
Netlogon Running
Hyper-V Heartbeat Service Running
Hyper-V Data Exchange Service Running
Hyper-V Remote Desktop Virtualization Service Running
Hyper-V Guest Shutdown Service Running
Hyper-V Volume Shadow Copy Requestor Running
Web Threat Defense Service Running
Web Threat Defense User Service_644de Running
Windows Management Instrumentation Running
管線
如本書的許多範例所示,您通常可以使用某個命令的輸出做為另一個命令的輸入。 在第 3 章中,Get-Member 用來判斷命令產生的物件類型。
第 3 章也會使用 的 Get-Command 參數來判斷哪些命令接受該類型的輸入。 取決於命令的說明有多詳細,它可能包含 INPUTS 和 OUTPUTS 一節。
INPUTS 區段顯示您可以將 ServiceController 或 String 物件傳送至 Stop-Service cmdlet。
help Stop-Service -Full
下列輸出已縮減為顯示相關的幫助部分。
...
INPUTS
System.ServiceProcess.ServiceController
You can pipe a service object to this cmdlet.
System.String
You can pipe a string that contains the name of a service to this
cmdlet.
OUTPUTS
None
By default, this cmdlet returns no output.
System.ServiceProcess.ServiceController
When you use the PassThru parameter, this cmdlet returns a
ServiceController object representing the service.
...
不過,它不會指定哪些參數接受這種類型的輸入。 您可以在 Stop-Service Cmdlet 的完整說明版本中檢查不同的參數,以判斷該資訊。
help Stop-Service -Full
再一次,只有相關的協助會顯示在下列結果中。 請注意,DisplayName 參數不接受管線輸入。
InputObject 參數會接受 ServiceController 物件的值形式管線輸入。 Name 參數會依值接受來自
...
-DisplayName <System.String[]>
Specifies the display names of the services to stop. Wildcard
characters are permitted.
Required? true
Position? named
Default value None
Accept pipeline input? False
Accept wildcard characters? true
-InputObject <System.ServiceProcess.ServiceController[]>
Specifies ServiceController objects that represent the services to
stop. Enter a variable that contains the objects, or type a command
or expression that gets the objects.
Required? true
Position? 0
Default value None
Accept pipeline input? True (ByValue)
Accept wildcard characters? false
-Name <System.String[]>
Specifies the service names of the services to stop. Wildcard
characters are permitted.
Required? true
Position? 0
Default value None
Accept pipeline input? True (ByPropertyName, ByValue)
Accept wildcard characters? true
...
處理管線輸入時,接受管線輸入的參數若同時可依屬性名稱 和依值 進行綁定,將優先進行依值 的綁定。 如果此方法失敗,它會嘗試依屬性名稱來處理管線輸入
例如,如果您使用管線將產生 ServiceController 物件的命令輸出傳送至 Stop-Service,則此輸出會系結至 InputObject 參數。 如果管線命令產生 String 物件,則會將輸出與 Name 參數產生關聯。 如果您使用管線從不會產生 ServiceController 或 String 物件的命令輸出,但包含名為 Name的屬性,Stop-Service 會將 Name 屬性的值系結至其 Name 參數。
判斷 Get-Service 命令產生的輸出類型。
Get-Service -Name w32time | Get-Member
Get-Service 會產生 ServiceController 物件類型。
TypeName: System.ServiceProcess.ServiceController
如 Stop-Service Cmdlet 的說明所示,InputObject 參數會透過管線 依值來接受 ServiceController 物件。 這表示當您使用管線將 Get-Service Cmdlet 的輸出傳送至 Stop-Service時,由 產生的 Get-Service 物件會系結至 的 Stop-Service 參數。
Get-Service -Name w32time | Stop-Service
現在請嘗試字串輸入。 使用管道將 w32time 傳送到 Get-Member 以確認它是一個字串。
'w32time' | Get-Member
TypeName: System.String
PowerShell 說明文件說明當您使用管線將字串傳送至 w32time 以管道傳送至 Stop-Service。 此範例示範 Stop-Service 如何處理字串 w32time 作為要停止的服務名稱。 執行以下命令以觀察此綁定和命令執行的實際運作。
請注意,w32time 以單引弧括住。 在 PowerShell 中,最佳做法是針對靜態字串使用單引號,並在字串包含需要展開的變數的情況下保留雙引號。 單引號會告訴 PowerShell 以字面方式處理內容,而不剖析變數。 這種方法不僅能確保腳本如何解譯字串的正確性,而且能增強效能,因為PowerShell會在單引號內消耗較少的字串處理工作。
'w32time' | Stop-Service
請建立一個自定義物件,根據屬性名稱來測試 的 Stop-Service 參數的管線輸入。
$customObject = [pscustomobject]@{
Name = 'w32time'
}
CustomObject 變數的內容是 PSCustomObject 物件類型,且包含名為 Name的屬性。
$customObject | Get-Member
TypeName: System.Management.Automation.PSCustomObject
Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
Name NoteProperty string Name=w32time
在此範例中使用 PowerShell 中的變數,例如 $customObject 時,如果您需要以引號括住變數,請務必使用雙引號。 雙引號允許變數展開 - PowerShell 會評估變數並使用其值。 例如,如果您以雙引弧括住 $customObject,並以管線將它傳送至 Get-Member,PowerShell 會處理 $customObject的值。 相反地,使用單引號會導致將字面字串 $customObject 傳送到 Get-Member,這不是變數的值。 對於需要評估變數值的案例而言,這一區別很重要。
當將 $customObject 是物件,其中包含名為 Name的屬性。 在此案例中,PowerShell 會識別 內的 $customObject 屬性,並使用其值作為 的 Stop-Service 參數。
使用不同的屬性名稱建立另一個自訂物件,例如 Service。
$customObject = [pscustomobject]@{
Service = 'w32time'
}
嘗試將 w32time 管線傳送至 $customObject以停止 Stop-Service 服務時發生錯誤。 管線系結失敗,因為 $customObject 不會產生 ServiceController 或 String 物件,而且不包含 Name 屬性。
$customObject | Stop-Service
Stop-Service : Cannot find any service with service name
'@{Service=w32time}'.
At line:1 char:17
+ $customObject | Stop-Service
+ ~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (@{Service=w32time}:String) [
Stop-Service], ServiceCommandException
+ FullyQualifiedErrorId : NoServiceFoundForGivenName,Microsoft.PowerShe
ll.Commands.StopServiceCommand
當某個命令的輸出屬性名稱不符合另一個命令的管線輸入需求時,您可以使用 Select-Object 來重新命名屬性名稱,使其正確排列。
在下列範例中,使用 Select-Object 將 Service 屬性重新命名為名為 Name的屬性。
第一眼看,此範例的語法可能會顯得複雜。 不過,請務必瞭解,僅僅複製和貼上程式碼並不足以瞭解語法。 相反地,請花時間手動輸入程序代碼。 此實際操作練習可協助您記住語法,而且會隨著反覆努力變得更直覺。 使用多個監視器或分割畫面也可以協助學習過程。 在一個畫面上顯示範例程式代碼,同時主動在另一個畫面上輸入並加以實驗。 此設定可讓您更輕鬆地遵循並增強您對語法的瞭解和保留。
$customObject |
Select-Object -Property @{Name='Name';Expression={$_.Service}} |
Stop-Service
在某些情況下,您可能需要使用不接受管線輸入的參數。 在這種情況下,您仍然可以使用某個命令的輸出做為另一個命令的輸入。 首先,擷取並儲存幾個特定 Windows 服務的顯示名稱到文本檔中。 此步驟可讓您使用已儲存的數據做為另一個命令的輸入。
'Background Intelligent Transfer Service', 'Windows Time' |
Out-File -FilePath $env:TEMP\services.txt
您可以使用括弧,將一個命令的輸出當做參數的輸入傳遞至另一個命令。
Stop-Service -DisplayName (Get-Content -Path $env:TEMP\services.txt)
這個概念就像代數中的運算順序。 就像先計算括弧內的數學運算一樣,以括弧括住的命令會在外部命令之前執行。
PowerShellGet
PowerShellGet,隨附於 PowerShell 5.0 版和更新版本的模組,提供命令來探索、安裝、更新及發佈 NuGet 存放庫中的 PowerShell 模組和其他專案。 針對使用 PowerShell 3.0 版和更新版本的使用者,PowerShellGet 也可作為個別下載使用。
PowerShell 畫廊 是由 Microsoft 所託管的線上資源庫,設計為共用 PowerShell 模組、腳本和其他資源的中央中樞。 雖然 Microsoft 託管 PowerShell 資源庫,但 PowerShell 社群為大部分可用的模組和腳本提供貢獻。 鑒於這些模組和腳本的來源,請謹慎考慮再將任何代碼整合到您的環境中,尤其是來自 PowerShell Gallery 的。 在隔離的測試環境中審查和測試從 PowerShell 資源庫 的下載。 此程式可確保程序代碼安全且可靠、如預期般運作,並保護您的環境免於因未經審查的程式代碼而產生的潛在問題或弱點。
許多組織選擇建立自己的內部私人 NuGet 存放庫。 此存放庫提供雙重用途。 首先,它作為一個安全的位置,用於儲存內部開發的模組,僅供內部使用。 其次,它會提供外部來源的已審查模組集合,包括來自公用存放庫的模組。 公司通常會進行徹底的驗證程式,再將這些外部模組新增至內部存放庫。 此程式對於確保模組不受惡意內容的影響,並與公司的安全性和作業標準保持一致非常重要。
使用屬於 Find-Module 模組的一部分的 Cmdlet,在 PowerShell 資源庫中尋找我撰寫的名為 MrToolkit的模組。
Find-Module -Name MrToolkit
NuGet provider is required to continue
PowerShellGet requires NuGet provider version '2.8.5.201' or newer to
interact with NuGet-based repositories. The NuGet provider must be available
in 'C:\Program Files\PackageManagement\ProviderAssemblies' or
'C:\Users\mikefrobbins\AppData\Local\PackageManagement\ProviderAssemblies'.
You can also install the NuGet provider by running 'Install-PackageProvider
-Name NuGet -MinimumVersion 2.8.5.201 -Force'. Do you want PowerShellGet to
install and import the NuGet provider now?
[Y] Yes [N] No [S] Suspend [?] Help (default is "Y"):
Version Name Repository Description
------- ---- ---------- -----------
1.3 MrToolkit PSGallery Misc PowerShell Tools
第一次使用 PowerShellGet 模組的其中一個命令時,系統會提示您安裝 NuGet 提供者。
若要安裝 MrToolkit 模組,請透過管道將上一個命令傳至 Install-Module。
Find-Module -Name MrToolkit | Install-Module -Scope CurrentUser
Untrusted repository
You are installing the modules from an untrusted repository. If you trust
this repository, change its InstallationPolicy value by running the
Set-PSRepository cmdlet. Are you sure you want to install the modules from
'https://www.powershellgallery.com/api/v2'?
[Y] Yes [A] Yes to All [N] No [L] No to All [S] Suspend [?] Help
(default is "N"):y
由於 PowerShell 資源庫是不受信任的存放庫,因此會提示您核准模組的安裝。
尋找管道輸入的簡便方法
MrToolkit 模組包含名為 Get-MrPipelineInput的函式。 此 Cmdlet 的設計目的是為使用者提供方便的方法,以識別能夠接受管線輸入之命令的參數。 具體來說,它揭示了三個關鍵層面:
- 命令的哪些參數可以接收管線輸入
- 每個參數接受的物件類型
- 無論它們是接受管線輸入 作為值,還是依屬性名稱
此功能能顯著簡化理解和利用 PowerShell 指令管線功能的過程。
透過分析說明檔先前取得的資訊,可以使用此函式來判斷。
Get-MrPipelineInput -Name Stop-Service | Format-List
ParameterName : InputObject
ParameterType : System.ServiceProcess.ServiceController[]
ValueFromPipeline : True
ValueFromPipelineByPropertyName : False
ParameterName : Name
ParameterType : System.String[]
ValueFromPipeline : True
ValueFromPipelineByPropertyName : True
總結
在本章中,您已經瞭解到 PowerShell 單行語句的複雜性。 您也了解到,命令的實際行數和將其分類為 PowerShell 單行命令無關。 此外,您已了解左側篩選、管道以及 PowerShellGet等重要概念。
回顧
- 什麼是 PowerShell 單行程式?
- PowerShell 中可以發生自然換行符的部分字元為何?
- 為什麼要向左過濾?
- PowerShell 命令可以接受管線輸入的兩種方式為何?
- 為什麼您不應該信任在 PowerShell 資源庫中找到的命令?
引用
後續步驟
在下一章中,您將瞭解格式化、別名、提供者和比較運算符。