PowerShell筆記 - 8.函數
互聯網 2021/9/16 7:07:47
本系列是一個重新學習PowerShell的筆記,內容引用自
PowerShell中文博客
處理函數的參數
Powershell函數可以接受參數,并對參數進行處理。函數的參數有3個特性:
- 任意參數:內部變量\(args 接受函數調用時接受的參數,\)args是一個數組類型。
- 命名參數:函數的每一個參數可以分配一個名稱,在調用時通過名稱指定對應的參數。
- 預定義參數:函數在定義參數時可以指定默認值,如果調用時沒有專門指定參數的值,就會保持默認值。
萬能參數$args
\(args 萬能參數
給一個函數定義參數最簡單的是使用\)args這個內置的參數。
它可以識別任意個參數。尤其適用哪些參數可有可無的函數。
function sayHello { if($args.Count -eq 0) { "No argument!" } else { $args | foreach {"Hello,$($_)"} } } Write-Host -ForegroundColor "Red" "無參數調用" sayHello Write-Host -ForegroundColor "Red" "一個或多個參數調用" sayHello hua sayHello hua hua PS C:\PowerShell>test.ps1 無參數調用 No argument! 一個或多個參數調用 Hello,hua Hello,hua Hello,hua
指定參數名及默認值
function Test($Str1 = "Hua",$str2 = "Hua") { $Str1 + $str2 } Test Test -Str1 "h" -str2 "ua" PS C:\PowerShell>test.ps1 HuaHua hua
指定參數類型
function SetDate([DateTime]$Date,[int]$Days = 0,[string]$Description = "無") { Write-Host -ForegroundColor "Green" $Date.Day $Date.AddDays($Days) Write-Host -ForegroundColor "Red" $Date.Day Write-Host -ForegroundColor "Red" $Description } SetDate '2020-01-01' SetDate '2020-11-01' -Days 6 -Description "HuaHua" #輸入錯誤的日期 SetDate '2020-15-01' -Days 6 -Description "HuaHua" PS C:\PowerShell>test.ps1 1 2020年1月1日 0:00:00 1 無 1 2020年11月7日 0:00:00 1 HuaHua SetDate : Cannot process argument transformation on parameter 'Date'. Cannot convert value "2020-15-01" to type "System.DateTime". Error: "String was not recognized as a valid DateTime." At test.ps1:11 char:9
Switch 參數
Powershell函數最簡單的參數類型為布爾類型,除了使用Bool類型,也可以使用Switch關鍵字。
下面的函數逆轉字符串,但是可以通過\(try 參數進行控制,如果沒有指定\)try的值,默認值為$false
function tryReverse( [switch]$try , [string]$source ) { [string]$target="" if($try) { for( [int]$i = $source.length -1; $i -ge 0 ;$i--) { $target += $source[$i] } return $target } return $source } tryReverse -source www.mossfly.com tryReverse -try $true -source www.mossfly.com PS C:\PowerShell> test.ps1 www.mossfly.com moc.ylfssom.www
指定函數的返回值
一個或多個返回值
Powershell不像它編程語言,它的函數可以有多個返回值。如果你直接調用函數,返回值會在控制臺輸出。當然你也可以將結果存儲在一個變量中進一步處理。
下面的例子演示返回一個值:
function GetDate() { return Get-Date } GetDate $d = GetDate $d.GetType().FullName PS C:\PowerShell> test.ps1 2021年9月14日 15:27:28 System.DateTime
下面的例子演示返回多個值
function GetDate() { $v = Get-Date $v.Year $v.Month $v.Day } GetDate $d = GetDate $d.GetType().FullName $d.Count $d[0] PS C:\PowerShell> test.ps1 2021 9 14 System.Object[] 3 2021
總結一下,如果一個函數返回一個值,像其它編程語言一樣,這個值包括她的類型信息會直接返回。但是如果遇到多個返回值,Powershell會將所有的返回值自動構造成一個Object數組??梢酝ㄟ^索引訪問數組。
Return語句
Powershell會將函數中所有的輸出作為返回值,但是也可以通過return語句指定具體的我返回值。
Return 語句會將指定的值返回,同時也會中斷函數的執行,return后面的語句會被忽略。
function GetDate() { $v = Get-Date $v.Year return $v.Month $v.Day } GetDate $d = GetDate $d.GetType().FullName $d.Count #return 語句之后的Day沒有返回被截斷 PS C:\PowerShell> test.ps1 2021 9 System.Object[]
訪問返回值
一個函數返回了一個值還是多個值,是可以驗證的。下面的例子會產生隨機數,如果沒有指定個數,默認會返回一個隨機數,否則會返回指定個數的隨機數。
Function lottery([int]$number=1) { $rand = New-Object system.random For ($i=1; $i -le $number; $i++) { $rand.next(1,50) } } # 參數為空時,返回值不是數組: $result = lottery $result -is [array] # False # 如果指定多個隨機數是,返回值是數組類型: $result = lottery 10 $result -is [array] PS C:\PowerShell> test.ps1 False True
從函數的返回值中消除輸出
函數默認會將函數中的所有輸出作為函數的返回值返回,這樣很方便。但有時可能會將不必要的輸出誤以為返回值。寫腳本程序時,可能需要自定義一些函數,這個函數可能只需要一個返回值,但是為了提高函數的可讀性,可能會在函數增加一些注釋輸出行。
Function Test() { "Try to calculate." "3.1415926" "Done." } #保存在變量中輸出, $value=Test $value # Try to calculate. # 3.1415926 # Done. #如果要過濾注釋,只輸出,不作為返回值, #可以使用Write-Host命令 Function Test() { Write-Host "Try to calculate." "3.1415926" Write-Host "Done." } # 在變量值中保存返回值,在控制臺輸出注釋行 $value=Test # Try to calculate. # Done. # 測試返回值 $value # 3.1415926
使用調試信息報告
可能輸出這些函數中臨時提示信息,給函數的返回值造成干擾。要解決這個問題,除了上述的Write-Host,也可以使用Write-Debug命令。
Function Test() { Write-Debug "Try to calculate." "3.1415926" Write-Debug "Done." } # Debug調試信息只會在調試模式下被輸出 $value=Test # 3.1415926 #如果你想通過顯示調試信息調試函數,可以開啟調試模式 $DebugPreference="Continue" $value=Test # 調試: Try to calculate. # 調試: Done. # 測試返回值 $value # 3.1415926 #如果關閉調試模式,這些調試信息自然不會輸出 $DebugPreference="SilentlyContinue" $value=Test
使用Write-Debug有兩個優勢,首先調試信息會自動高亮顯示,便于分析。其次,這些調試信息只會在調試模式開啟時輸出,控制起來更加方便。當然最重要的是這些臨時信息無論什么時候也不會混淆在返回值。
抑制錯誤信息
函數中的錯誤信息,也有可能作為返回值的一部分,因為默認這些錯誤信息會直接輸出。
Function ErrorTest() { #該進程不存在 Stop-Process -Name "www.mossfly.com" } ErrorTest Stop-Process : 找不到名為“www.mossfly.com”的進程。請驗證該進程名稱,然后再次調用 cmdlet。 所在位置 C:UsersbaozhenDesktoptest.ps1:6 字符: 17 + Stop-Process <<<< -Name "www.mossfly.com" + CategoryInfo : ObjectNotFound: (www.mossfly.com:String) [Stop-P rocess], ProcessCommandException + FullyQualifiedErrorId : NoProcessFoundForGivenName,Microsoft.PowerShell. Commands.StopProcessCommand 很明顯,類似這樣的錯誤提示信息,對調試程序很重要,但如果你覺得它不重要,特意要隱藏,可以使用$ErrorActionPreference進行設置。 Function ErrorTest() { #從這里開始隱藏所有的錯誤信息 $ErrorActionPreference="SilentlyContinue" Stop-Process -Name "www.mossfly.com" #該進程不存在 } #錯誤信息不會輸出 ErrorTest
但是上面的做法并不明智,因為這樣可能錯過其它錯誤提示。所以最好的方式是處理完后,對$ErrorActionPreference進行復位。
Function ErrorTest() { #從這里開始隱藏所有的錯誤信息 $ErrorActionPreference="SilentlyContinue" Stop-Process -Name "www.mossfly.com" #該進程不存在 #恢復$ErrorActionPreference,錯誤開始輸出 $ErrorActionPreference="Continue" 2/0 } ErrorTest 試圖除以零。 所在位置 行:9 字符: 7 + 2/ <<<< 0 + CategoryInfo : NotSpecified: (:) [], ParentContainsErrorRecordException + FullyQualifiedErrorId : RuntimeException
查看支持的函數
Powershell已經提供了許多用戶能夠使用的預定義函數,這些函數可以通過Function:PSDrive虛擬驅動器查看。
PS C:\PowerShell> Get-ChildItem function: | Format-Table Name,Definition Name Definition ---- ---------- A: Set-Location $MyInvocation.MyCommand.Name B: Set-Location $MyInvocation.MyCommand.Name C: Set-Location $MyInvocation.MyCommand.Name cd.. Set-Location .. cd\ Set-Location \ Clear-Host ... ConvertFrom-SddlString ... D: Set-Location $MyInvocation.MyCommand.Name E: Set-Location $MyInvocation.MyCommand.Name F: Set-Location $MyInvocation.MyCommand.Name Format-Hex ... G: Set-Location $MyInvocation.MyCommand.Name Get-FileHash ... Get-Verb ... H: Set-Location $MyInvocation.MyCommand.Name help ... I: Set-Location $MyInvocation.MyCommand.Name Import-PowerShellDataFile ... ImportSystemModules J: Set-Location $MyInvocation.MyCommand.Name K: Set-Location $MyInvocation.MyCommand.Name L: Set-Location $MyInvocation.MyCommand.Name M: Set-Location $MyInvocation.MyCommand.Name mkdir ... more ... N: Set-Location $MyInvocation.MyCommand.Name New-Guid ... New-TemporaryFile ... O: Set-Location $MyInvocation.MyCommand.Name oss ... P: Set-Location $MyInvocation.MyCommand.Name Pause $null = Read-Host 'Press Enter to continue...' prompt ... PSConsoleHostReadLine ... Q: Set-Location $MyInvocation.MyCommand.Name R: Set-Location $MyInvocation.MyCommand.Name S: Set-Location $MyInvocation.MyCommand.Name T: Set-Location $MyInvocation.MyCommand.Name TabExpansion2 ... U: Set-Location $MyInvocation.MyCommand.Name V: Set-Location $MyInvocation.MyCommand.Name W: Set-Location $MyInvocation.MyCommand.Name X: Set-Location $MyInvocation.MyCommand.Name Y: Set-Location $MyInvocation.MyCommand.Name Z: Set-Location $MyInvocation.MyCommand.Name
從這些結果不但能夠看出函數的名稱,還能通過Definition列查看函數的內容。如果你想深入查看函數的內部定義可以直接訪問Function:
PS C:\PowerShell> $function:prompt "PS $($executionContext.SessionState.Path.CurrentLocation)$('>' * ($nestedPromptLevel + 1)) "; # .Link # https://go.microsoft.com/fwlink/?LinkID=225750 # .ExternalHelp System.Management.Automation.dll-help.xml
Powershell中的這些預定義的函數可以做很多重要的工作。
Clear-Host 清除屏幕的緩存
help,man 查看命令的幫助文檔
mkdir,md 通過new-Item創建子目錄
more 分屏輸出管道結果
prompt 返回提示文本
TabExpansion Tab鍵的自動完成提示
X: 調用Set-Location定位到指定的驅動器根目錄
如果你想查看當前Powershell環境中定義了多少個函數可以通過:
PS C:\PowerShell> (Dir function:).Count 45
自定義Prompt
每次成功執行完一條命令,Powershell就會執行Prompt函數,提示用戶進行下一步輸入。
默認設置中,prompt顯示“PS” 和當前的工作目錄。
再接著是”>”或”>>”,具體情況要看當前Powershell控制臺的的層數。
當然你可以自定義prompt的,那就得覆蓋prompt函數:
PS C:\PowerShell> pwd Path ---- C:\PowerShell PS C:\PowerShell> Function prompt{"Hua Hua"} Hua Hua Hua Huapwd Path ---- C:\PowerShell Hua Hua
這樣的覆蓋安全嗎,顯然安全,對預定義函數的重寫,只會在當前控制臺會話中有效,當你重新啟動控制臺時,自然會恢復如初。
在控制臺的任何位置輸出文本(自定義光標的位置)
因為控制臺的內容存放在控制臺屏幕的緩存中,因此你可以逐個訪問內容的每一行或每一個字符。
你甚至可以在控制臺的屏幕的任何位置輸出你想要輸出的信息,接下來的函數會演示這個功能。
要完成這個功能,需要使用$Host.UI.Rawui ,光標的位置通過屏幕的橫坐標(X)和縱坐標(Y)確定,下面的函數會首先記住當前光標的位置,然后在橫坐標上增加60個占位符,然后重置光標的位置至當前位置,最后通過prompt函數回復光標的原始位置。
Hua H> function prompt >> { >> $curPos = $host.ui.rawui.CursorPosition >> $newPos = $curPos >> $newPos.X+=60 >> $host.ui.rawui.CursorPosition = $newPos >> Write-Host ("{0:D} {0:T}" -f (Get-Date)) -foregroundcolor Yellow >> $host.ui.rawui.CursorPosition = $curPos >> Write-Host ("PS " + $(get-location) +">") -nonewline -foregroundcolor Green >> " " >> } PS C:\PowerShell> 2021年9月14日 16:57:04
使用窗口標題欄
在Windows控制臺的標題欄有一部分空間,可以放置一些有用的信息,比如當前哪個用戶登錄在控制臺,可以通過設置$host.UI.RawUI.WindowTitle
來自定義控制臺標題欄的文本。
下面的例子就會演示設置標題欄文本,通過.NET方法獲取當前用戶信息,由于該方法會有幾秒鐘執行時間,為了效率考慮首先將用戶信息保存在全局變量中,然后在Prompt函數中調用。
$global:CurrentUser = [System.Security.Principal.WindowsIdentity]::GetCurrent() function prompt { $host.ui.rawui.WindowTitle = "Line: " + $host.UI.RawUI.CursorPosition.Y + " " + $CurrentUser.Name + " " + $Host.Name + " " + $Host.Version Write-Host ("PS " + $(get-location) +">") -nonewline -foregroundcolor Green return " " }
執行以后在標題欄會顯示:Line: 72 ComputerNameuser ConsoleHost 2.0
如果你使用管理員權限運行控制臺時,Prompt函數還可以給出警告。使用WindowsPrincipal 辨別當前用戶是否使用了管理員權限,你不需要了解下面的.NET代碼,它會在全局變量中將布爾值賦值給$Admin。
$CurrentUser = [System.Security.Principal.WindowsIdentity]::GetCurrent() $principal = new-object System.Security.principal.windowsprincipal($CurrentUser) $global:Admin = $principal.IsInRole( [System.Security.Principal.WindowsBuiltInRole]::Administrator) Function prompt { # 輸出標準的提示信息: Write-Host ("PS " + $(get-location)) -nonewline # The rest depends on whether you have admin rights or not: If ($admin) { $oldtitle = $host.ui.rawui.WindowTitle # 將"Administrator: " 顯示在標題欄 If (!$oldtitle.StartsWith("Administrator: ")) { $host.ui.rawui.WindowTitle ="Administrator: " + $oldtitle } # Prompt結尾顯示紅色的尖括號 Write-Host ">" -nonewline -foregroundcolor Red } Else { Write-Host ">" -nonewline } return " " }
沒有管理員權限時,標題欄文本:Windows Powershell
有管理員權限時,標題欄文本:Administrator :管理員 : Windows Powershell
Clear-Host:刪除屏幕緩存
很可能,你已經注意到了,cls可以刪除屏幕的緩存。
事實上,cls只是Clear-Host函數的別名,但是卻看不到這個函數的內容。
PS C:\PowerShell> $function:Clear-Host At line:1 char:16 + $function:Clear-Host + ~~~~~ Unexpected token '-Host' in expression or statement. + CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException + FullyQualifiedErrorId : UnexpectedToken
在Powershell中短斜杠是個特殊字符,如果一個函數名中包含了特殊字符就應當把它放在花括號中。
PS C:\PowerShell> ${function:Clear-Host} $RawUI = $Host.UI.RawUI $RawUI.CursorPosition = @{X=0;Y=0} $RawUI.SetBufferContents( @{Top = -1; Bottom = -1; Right = -1; Left = -1}, @{Character = ' '; ForegroundColor = $rawui.ForegroundColor; BackgroundColor = $rawui.BackgroundColor}) # .Link # https://go.microsoft.com/fwlink/?LinkID=225747 # .ExternalHelp System.Management.Automation.dll-help.xml
盤符名預定義函數C:,D:,E:
這些盤符名稱可以作為單獨的一個函數,是怎么做到的呢?
PS C:\PowerShell> $function:C: Set-Location $MyInvocation.MyCommand.Name
函數過濾器、管道
一個函數能夠訪問和進一步處理另外一條命令的結果嗎?答案是肯定的,這被稱為管道。管道有兩種模式,一種是順序處理模式,一種是流處理模式。
低效率的順序模式:$input
在最簡單的情況下,你的函數不是真正支持管道。只能對前一個命令執行后的結果處理。前一個命令執行的結果通過被自動保存在$input
變量中,$input
是一個數組,它可以包含許多元素,一個元素,甚至一個元素都沒有,這取決于具體的環境。
下面的例子,是一個函數,僅僅輸出$input
的內容。
PS C:\PowerShell> function OutPut { >> $input >> } PS C:\PowerShell> 1,2,3 | OutPut 1 2 3 PS C:\PowerShell> "222",1 | OutPut 222 1 PS C:\PowerShell> dir | OutPut Directory: C:\PowerShell Mode LastWriteTime Length Name ---- ------------- ------ ---- -a---- 2021/9/14 13:51 58 test.txt
到目前為止,這個函數只是僅僅輸出了管道的結果,并沒有其它比較強大的功能。
在接下來的例子中,函數將會對管道的結果做進一步處理。函數名MarkEXE,將會檢查Dir的結果,并高亮標記后綴名為EXE的文件名為紅色。
Function MarkEXE { # 保存控制臺當前的前景色 $oldcolor = $host.ui.rawui.ForegroundColor # 通過循環逐條檢查管道的結果 Foreach ($element in $input) { # 如果后綴名為.exe,設置為前景色為紅色 If ($element.name.toLower().endsWith(".exe")) { $host.ui.Rawui.ForegroundColor = "red" } Else { # 否則恢復默認的前景色 $host.ui.Rawui.ForegroundColor = $oldcolor } # 輸出數組元素 $element } # 最后,重置控制臺的前景色: $host.ui.Rawui.ForegroundColor = $oldcolor } Dir | MarkEXE PS C:\PowerShell> New-Item test.exe Directory: C:\PowerShell Mode LastWriteTime Length Name ---- ------------- ------ ---- -a---- 2021/9/15 10:46 0 test.exe PS C:\PowerShell> ls Directory: C:\PowerShell Mode LastWriteTime Length Name ---- ------------- ------ ---- -a---- 2021/9/15 10:46 0 test.exe -a---- 2021/9/14 13:51 58 test.txt PS C:\PowerShell> test.ps1 Directory: C:\PowerShell Mode LastWriteTime Length Name ---- ------------- ------ ---- -a---- 2021/9/15 10:46 0 test.exe -a---- 2021/9/14 13:51 58 test.txt
過濾器:高效率 流模式
管道的低效率順序模式在處理大容量數據時很容易出現問題,其結果是巨大的內存占用和進程等待。
如果你的函數支持高效率的流模式,在處理管道結果時僅占用很小的內存。
事實上,針對之前MarkEXE函數,你只需要替換”function” 關鍵字 為 “filter”它就會開始流模式處理,這樣你再也不用過分的擔心忍受程序的無休止的響應和崩潰的危險。
你也可以遞歸處理全盤目錄,甚至處理極其龐大的數據。例如:
PS C:\PowerShell> Dir C:\PowerShell\ -recurse | MarkEXE
當MarkEXE每次被調用時,它只會對當前目錄下的每個單獨的元素進行處理。
對于過濾器filters
來說,$input
一直都是一個獨立的元素。
這也就是為什么在過濾器中$input
一點用也沒有的道理。
此時,最好使用$_
變量,因為它代表了當前處理的數據。
這樣還可以簡化MarkExe,因為過濾器自身已經扮演了循環的角色了,你沒有必要再寫專門的循環處理了。
Filter MarkEXE { # 記錄當前控制臺的背景色 $oldcolor = $host.ui.rawui.ForegroundColor # 當前的管道元素保存在 $_ 變量中 # 如果后綴名為 ".exe", # 改變背景色為紅色: If ($_.name.toLower().endsWith(".exe")) { $host.ui.Rawui.ForegroundColor = "red" } Else { # 否則使用之前的背景色 $host.ui.Rawui.ForegroundColor = $oldcolor } # 輸出當前元素 $_ # 最后恢復控制臺顏色: $host.ui.Rawui.ForegroundColor = $oldcolor } Dir | MarkEXE PS C:\PowerShell> test.ps1 Directory: C:\PowerShell Mode LastWriteTime Length Name ---- ------------- ------ ---- -a---- 2021/9/15 10:46 0 test.exe -a---- 2021/9/14 13:51 58 test.txt
開發真正的管道函數
過濾器在函數中屬于高級應用,因為它可以立即處理管道結果的每一個元素。但是過濾器必須每次重復執行預定義命令的結果。
對于MarkEXE 函數,每次執行的過程中要記錄和更新控制臺的背景顏色,也要花費資源和時間。
事實上,過濾器只是特殊的函數。如果一個函數內部使用了管道,你就可以定義三個基礎的任務區了:第一步,完成函數的初始化,完成函數執行的預備步驟;第二步處理遞歸調用所得的結果;最后進行收尾工作。
這三個任務區分別可以使用begin
,process
,end
語句塊。
function MarkEXE { begin { # 記錄當前控制臺的背景色 $oldcolor = $host.ui.rawui.ForegroundColor } process { # 當前的管道元素保存在 $_ 變量中 # 如果后綴名為 ".exe", # 改變背景色為紅色: If ($_.name.toLower().endsWith(".exe")) { $host.ui.Rawui.ForegroundColor = "red" } Else { # 否則使用之前的背景色 $host.ui.Rawui.ForegroundColor = $oldcolor } # 輸出當前元素 $_ } end { # 最后恢復控制臺顏色: $host.ui.Rawui.ForegroundColor = $oldcolor } } Dir | MarkEXE PS C:\PowerShell> test.ps1 Directory: C:\PowerShell Mode LastWriteTime Length Name ---- ------------- ------ ---- -a---- 2021/9/15 10:46 0 test.exe -a---- 2021/9/14 13:51 58 test.txt

關于找一找教程網
本站文章僅代表作者觀點,不代表本站立場,所有文章非營利性免費分享。
本站提供了軟件編程、網站開發技術、服務器運維、人工智能等等IT技術文章,希望廣大程序員努力學習,讓我們用科技改變世界。
[PowerShell筆記 - 8.函數]http://www.yachtsalesaustralia.com/tech/detail-228809.html
- 2022-08-12powershell修改編碼
- 2022-08-09powershell無法執行腳本
- 2022-07-13在Windows 10 , windows 7 上開啟 Powershell 遠程功能
- 2022-06-22powershell無文件攻擊場景匯總
- 2022-06-16ESXI系列問題整理以及記錄——使用Windows PowerShell中的SSH功能連接ESXI控制臺
- 2022-05-24PowerShell 筆記 - 輸出格式化
- 2022-05-24PowerShell 筆記 - 管道進階
- 2022-05-15Windows Powershell個性化設置
- 2022-05-14window10 powershell ssh登錄提示Bad owner or permissions
- 2022-05-12PowerShell SSH 連接 VirtualBox Ubuntu 虛擬機的具體步驟