PowerShell 筆記 - 變數有效範圍
1 | 2,318 |
區域變數、全域變數是各種程式語言都有的基本概念,PowerShell 也不例外。寫程式因觀念不清被迷惑,研究後發現它跟 C#、JavaScript 有些不同,特別寫篇筆記備忘。
參考資料:
SS64 將 PowerShell Scope 分為四種:
- Global - 從 PowerShell 一啟動就存在,預設變數、自動變數以及 Profile 建立的變數別名都放在這區。
- Local - 目前所在的 Scope,可能是 Global、Script 或是任何 Scope
- Script - ps1 或 ScriptBlock 形成的 Scope
- 階層數字 - Scope 有巢狀關係,有父 Scope 跟子 Scope 之分,0 代表目前所在的 Scope,1 代表父層 Scope、2 代表祖父層 Scope,以此類推。
不同 Scope 間存取規則如下:
- Scope 有巢狀關係,子 Scope 可以看到父 Scope 的變數
- 在該 Scope 所建立的變數只有該 Scope 及其子 Scope 可以看到,設成 private 可防止其他 Scope 存取
- 變數只能被建立它的 Scope 修改,子 Scope 能讀不能改 (除非指定 Scope)
第三點跟 JavaScript 或 C# 比較不同,是讓迷惑的原因,看完文件才弄懂。來個範例演練一下:
$x = "x from outside"
$y = "y from outside"
$g = "g from outside"
function func() {
Write-Host "inside func()" -ForegroundColor Yellow
Write-Host "x = $x"
$y = "y from inside"
Write-Host "y = $y"
$z = "z from inside"
Write-Host "z = $z"
$global:g = "g from inside"
Write-Host "g = $g"
Write-Host "global:g = $global:g"
}
func
Write-Host "outside func()" -ForegroundColor Yellow
Write-Host "x = $x"
Write-Host "y = $y"
Write-Host "z = $z"
Write-Host "g = $g"
Write-Host "global:g = $global:g"
我在 test1.ps1 宣告了 $x, $y, $g 三個變數,在 function func() 中讀取 $x、修改 $y、宣告一個 $z 及 $global:g 變數。func() 內部會形成一個子 Scope,藉此觀察父子 Scope 間的變數存取行為。
測試時先呼叫 func() 檢查及修改這些變數內容,再檢查父 Scope 變數的狀況。
實測結果如下:
func() 的區域 Scope 可以看到上一層的 $x、$y、$g。區域 Scope 設定的 $z,父 Scope 看不到。比較有趣的是,對 $y 的修改只有在 func() 執行期間有效,結束後父 Scope 看到的 $y 還是原本的值。可以想成子 Scope 為變數複製一個分身,子 Scope 對分身做的修改並不影響本尊,這點跟 JavaScript 或 C# 子 Scope 存取父 Scope 變數概念不太一樣,值得留意。另外,子 Scope 跟父 Scope 可用 $global:g 共用 Global 變數,可讀可寫。而 $globa:g 與 test1.ps1 的 $g 不是同一個變數,test1.ps1 的 $g 相當於 $script:g。
以下這個例子更清楚地展示了 $global:v、$script:v、$local:v (等同 $v) 的差異:
如何在 func() 裡更改 .ps1 層變數的內容?由上面測試可知,寫成 $script:variableName = "..." 就可以了,若是較複雜的巢狀結構,可透過 Set-Variable -Scope 1 修改父層或祖父、曾祖父層的變數,如以下示範:
$x = "x from outside"
function func() {
Write-Host "inside func()" -ForegroundColor Yellow
Write-Host "x = $x"
Write-Host "set x as new value"
# Scope 0 - current or local
# Scope 1 - parent scope
# Scope 2 - parent scope of parent
Set-Variable -Name x -Value "x from inside" -Scope 1
Write-Host "x = $x"
}
func
Write-Host "outside func()" -ForegroundColor Yellow
Write-Host "x = $x"
不過,使用全域變數或更動上層變數的做法,易造成變值異動難以追蹤、讓程式不易理解、提高相依性妨礙重構或函式重複利用,通常被視為不好的設計方式。故要在函式內修改外部 Scope 變數的另一種做法是函式參數 加註 [ref],接著在函式內部即可使用 $refVarName.Value 存取及修改變數內容,如下示範:
$x = "x from outside"
function func([ref]$v) {
Write-Host "inside func()" -ForegroundColor Yellow
Write-Host "v = " $v.Value
Write-Host "set new value"
$v.Value = "value from insdie"
Write-Host "v = " $v.Value
}
func ([ref]$x)
Write-Host "outside func()" -ForegroundColor Yellow
Write-Host "x = $x"
以上就是針對 PowerShell Scope 的簡單介紹,提供給未來忘記這是怎麼一回事的我大家參考。
Introduction to the basic concept of PowerShell scope.
Comments
# by Huang
ref感覺像call by reference power shell的變數範圍有集大成的感覺,朝向簡單化使用但取了嚴謹的優點來避免靈異件