[PowerShell] 快速入門, 基本語法, 常用型別, 函式, .NET 互操作

SlimeNull發表於2021-05-11

PowerShell 快速入門

開始之前, 我們認定你已經有一定的程式設計基礎, 熟悉 .NET 中的型別與物件.

此文章對於 .NET 開發者來說更簡單哦!

在 PowerShell 中, 幾乎一切都是物件. 與 CMD 有很大不同. PowerShell 是強型別的, 它基於 .NET, 故, PowerShell 可以近乎完美的呼叫 .NET 的標準庫.

0. 準備工作

- 官方文件

既然要學新東西, 肯定要會查閱官方文件才彳亍呀! 本文章參閱官方文件, 並使用更簡單的語言講述給讀者, 在每一部分都會有推薦的官方文件連結目錄, 點選即可跳轉.

MSDocs PowerShell : 如何使用 PowerShell 官方文件

MSDocs PowerShell : PowerShell 是什麼

MSDocs PowerShell : 關於主題 (涵蓋有關 PowerShell 的一系列概念)

另外, 在文件的中文頁面的左側索引部分, 你無法看到所有的內容, 例如 Reference 分類, 其中包含了非常多的 '隱藏' 內容, 只有你切換到英文頁面才可以看到它們.

至於如何切換頁面語言, 也簡單, 只需要將位址列中的 'zh-cn' 改為 'en-us' 即可, 同理, 如果某個頁面你看不懂, 想要切換到中文, 可以將 'en-us' 改為 'zh-cn', 不過需要注意的是, 部分頁面是沒有提供中文翻譯的.

為了方便, 本文涉及到的所有文件連結均為英文直鏈, 如需訪問中文頁面, 請自行修改連結地址.

- 啟動 PowerShell

PowerShell 同 CMD 一樣有許多啟動方式, 準備好開始了嗎? 那麼開始吧!

  1. 按下 "Win" + "R" 開啟 '執行' 視窗, 鍵入 'PowerShell' 並確認.

  2. 按 "Win" + X, 然後按 'I', 或者按 'A'(以管理員身份執行).

  3. 在桌面或資源管理器, 不選中任何條目, 按住 Shift, 右擊背景, 在彈出的選單中選擇 '在此處啟動 PowerShell'.

  4. 在資源管理器中, 在位址列輸入 'PowerShell' 並按 Enter 鍵確認.

接下來, 出現的藍底白字的控制檯頁面就是 PowerShell 的主程式了! 在其中輸入指令(程式碼)可以執行一些東西.

1. 基本語法

- 標準輸出

讓我們從一個 'hello world' 的輸出開始吧, 與 CMD, Bash 一樣, 使用 echo 指令是可以輸出內容的.

echo "Hello World!"

輸出:

Hello World!

- 獲取幫助:

執行以下指令來獲取關於 echo 的幫助內容.

Get-Help echo

輸出:

NAME
    Write-Output

SYNTAX
    Write-Output [-InputObject] <psobject[]> [-NoEnumerate]  [<CommonParameters>]


ALIASES
    write
    echo


REMARKS
    Get-Help cannot find the Help files for this cmdlet on this computer. It is displaying only partial help.
        -- To download and install Help files for the module that includes this cmdlet, use Update-Help.
        -- To view the Help topic for this cmdlet online, type: "Get-Help Write-Output -Online" or
           go to https://go.microsoft.com/fwlink/?LinkID=113427.     

看到結果, 你懂了吧, echo 實際上是 Write-Output 的別名, 同時也可以用 write 來執行.

另外, PowerShell 的指令是不區分大小寫的, 所以 Write-Output 與 wRITE-oUTPUT 是一樣執行效果的

- 定義變數

PowerShell 官方文件 : 關於變數

定義變數的方式與 Bash 差不多, 都是用 $ 開頭, 後跟變數名

$var = 123
echo $var

輸出:

123

- 固定常量

PowerShell 中有一些常量, 不可被賦值 (如果對其賦值, 不會產生任何作用, 且不會丟擲異常)

常量名 型別
$null object null
$true bool true
$false bool false

如果訪問一些未定義的變數, 返回的值同樣是 null

- 型別概念

PowerShell 是強型別的. 例如剛剛我們定義的變數, 其實就是一個 int 變數. 示例:

$a = 123
$b = 321
$c = $a + $b
echo $c

輸出:

444

數字支援運算, 所以理所當然的可以進行運算.

- 表示式與指令

剛剛我們所用的 echo 指令, 同時可以說是一個表示式. 而剛剛我們進行加法運算時, 所使用的 '$a + $b' 通用

我沒猜錯的話, 在剛剛的變數部分, 也同樣有人這麼執行:

$a = 123
$b = 321
echo $a + $b

然後結果就是, 輸出了

$a + $b

而解決辦法就是, 為這個表示式加上括號, 優先運算這個表示式, 於是就正常了, 或者你可以直接 echo 一個表示式:

echo (123+321)

輸出

444

- 運算子

剛剛我們從嘗試了加法運算, 同時在 PowerShell 中, 也是支援減法, 乘法, 除法的, 除此之外還有小於, 大於, 等於, 大於等於, 小於等於:

echo (1 -lt 10)

輸出:

True
運算子 描述
-eq 等於
-ne 不等於
-lt 小於
-gt 大於
-le 小於等於
-ge 大於等於

2. 流程控制:

- 基本判斷

MSDocs PowerShell : 關於 If

判斷的話, 毋庸置疑是 if 語句. 當然, 無非是 'if' 'else' 'else if'

在 PowerShell 中, if 語句的語法可以是這樣:

if (exp) {
    statement block
} elseif {
    statement block
} else {
    statement block
}
# 示例 :
$randint = (New-Object Random).Next()    # 生成一個隨機數, 並賦值給 randint 變數
if ($randint -eq 114514) {
    echo "嗯哼哼哼啊啊啊啊啊啊啊啊"
} elseif ($randint -gt 114514) {
    echo "吔屎了你, 樑非凡!"
} else {
    echo "聽不見! 就這點聲音還想開軍艦!?"
}

沒錯, PowerShell 中, 'else if' 的話是不允許帶空格的. 並且需要注意的是, 這個大括號不允許省略.

- 迴圈語句

for 迴圈

MSDocs PowerShell : 關於 For

for (exp; exp; exp) {
    statement block
}
# 示例 :
for ($i = 0; $i -lt 10; $i++) {
    echo $i
}
for ($i = 1; $i -lt 10; $i++) {
    for ($j = 1; $j -le $i; $j++) {
        [Console]::Write(("{0}x{1}={2}`t" -f ($j, $i, $i * $j)))   # 不換行輸出
    }
    [Console]::WriteLine()    # 換行
}

foreach 迴圈

MSDocs PowerShell : 關於 Foreach

foreach ($var in enumerable object) {
    statement block
}
# 示例 :
foreach ($num in @(1..5)) {
    echo $num
}

while 迴圈

MSDocs PowerShell : 關於 While

while (exp) {
    statement block
}

打破迴圈:

MSDocs PowerShell : 關於 Break

# 在 for 中打破迴圈 :
for ($i = 0; $i -lt 10; $i++) {
    if ($i -eq 5) {
        break
    }
    echo $i
}

# 在 foreach 語句中打破迴圈 :
foreach ($i in @(1..10)) {
    if ($i -eq 5) {
        break
    }
    echo $i
}

# 在 while 語句中打破迴圈 :
$i = 0
while ($true) {
    if ($i -eq 5) {
        break
    }
    echo $i
    $i++
}

3. 常用型別

- 物件陣列

MSDocs PowerShell : 關於陣列

MSDocs PowerShell : 關於陣列的各項須知內容

在 PowerShell 中, 當然也存在陣列, 通過以下方式來建立:

# 語法 1:
$var = @(element 1, element 2, element 3, ...., element n)
# 示例:
$array = @(1, 3, 4, 5)

# 語法 2:
$var = start..end
# 示例:
$array = @(1..5)    # 生成的陣列等同於: @(1, 2, 3, 4, 5)

# 提示, @() 是可以省略的. 不過如果要建立空陣列, 不可省略

陣列的訪問, 與其他大多數程式語言一致, 通過中括號就可以:

echo @(1..4)[2]   # 輸出 3
$array = @(1..4)
$array[2] = 114514
echo $array[2]    # 輸出 114514

PowerShell 的陣列還支援獲取多個值, 它返回一個新的陣列:

$array = @(1..4)
$array2 = $array[0, 2]
echo $array2[0]   # 輸出 1
echo $array2[1]   # 輸出 3

新增元素, 可以用 '+=' 運算子, 但注意, 不推薦這麼做, 因為每為陣列新增元素, 實際上都是重新建立了新的陣列, 如果需要更靈活的陣列, 請使用 泛型List

$array = @(1..4)
$array += 114514
echo $array[4]    # 輸出 114514

如果獲取陣列元素值時, 索引超出界限, 不會丟擲異常, 會返回 null:

$array = @(1..4)
echo $array[114514]  # 不會有任何輸出, 因為值是null

如果對一個不是陣列的變數使用索引操作符, 則會丟擲異常:

$test = $null
$test[0]          # 丟擲異常

兩個陣列相加, 返回陣列連線後的結果:

$array1 = @(1..3)
$array2 = @(3..1)
echo ($array1 + $array2)

輸出:

1
2
3
3
2
1

使用 -contains 操作符來判斷陣列是否包含某元素:

$array = @(1..4)
echo ($array -contains 2)    # 輸出 True

在 PowerShell 的陣列中, -eq 操作符將對所有元素進行判斷, 即, 遍歷內容, 逐一進行 -eq 運算, 如若有任何元素運算返回 True, 則表示式返回真.

$array = @(1..4)
echo ($array -eq 3)          # 返回 True

如果判斷一個變數(可能是陣列)為空, 請不要將這個變數放在左側:

$array -eq $null      # 不要這樣使用
$null -eq $array      # 正確的使用方式

使用 Count 來獲取一個陣列的長度, 但注意, 如果一個變數值為null, 同樣可以使用 count 來獲取長度, 它返回 0:

$array = @(1..4)
echo $array.Count     # 返回 4
echo $null.Count      # 返回 0

你也可以通過 Length 來獲取一個陣列的長度, 它與 Count 用法一樣, 並且如果是 $null, 同樣返回 0.

- 雜湊表

MSDocs PowerShell : 關於雜湊表

MSDocs PowerShell : 關於雜湊表的一些注意事項

雜湊表, 即 HashTable. 在 PowerShell 中這樣建立 HashTable:

$table = @{
    key = value
    key = value
}
# 示例 :
$table = @{}            # 建立空表
$table = @{             # 建立有初始值的表, 下面的示例將使用這個表
    "NullSlime" = 100
    "MoYuro" = 96
    "Muchen" = 94
    "KingHans" = 88
}

訪問雜湊表, 通過索引運算子, 指定鍵:

# 使用剛剛建立的 table
echo $table["MoYuro"]    # 輸出 96
$table["MoYuro"] = 97
echo $table["MoYuro"]    # 輸出 97

向表中新增鍵值對, 使用 Add 方法:

$table.Add(key, value)
# 示例 :
$table.Add("KunLong", 76)

刪除表中的某個鍵值對, 使用 Remove 方法:

$table.Remove(key)
# 示例 :
$table.Remove("KingHans")

雜湊表和陣列一樣支援選擇多個值, 只需要在索引運算子中指定多個鍵:

$table[key1, key2, ... , key n]
$table[(key1, key2, ... , key n)]
$table[@(key1, key2, ... , key n)]
# 示例 :
$values = $table["NullSlime", "MoYuro"]    # 返回一個陣列
echo $values[0]                            # 輸出 100
echo $values[1]                            # 輸出 96

通過 Keys 與 Values 來獲取這個雜湊表的所有鍵以及值

$keys = $tables.Keys          # 返回雜湊表的所有鍵
$values = $tables.Values      # 返回雜湊表的所有值

可以通過對雜湊表的列舉器來進行迴圈雜湊表的每一個鍵值對:

foreach ($pair in $table.GetEnumerator()) {
    echo ("{0}: {1}" -f ($pair.Key, $pair.Value))
}

當通過foreach對雜湊表進行迭代時, 不可以對雜湊表進行刪減:

4. 指令碼函式

- 關於函式

PowerShell 官方文件 : 關於函式

PowerShell 中定義函式很簡單, 它的本質是指令碼塊(ScriptBlock):

function name(parameters) {
    statement block
}
# 示例 :
function myFunc($a, $b) {
    echo ($a + $b)
}

呼叫 PowerShell 中定義的函式, 語法如下:

name argv1 argv2 argv3 .... argv n
# 示例 :
myFunc 114000 514    # 輸出 114514

如果要獲取這個函式的物件引用 可以這樣:

$function:name
# 示例 :
echo $function:myFunc

輸出:

param($a, $b)

echo ($a + $b)

通過函式的物件引用, 可以使用 Invoke 來呼叫:

$function:name.Invoke(parameters)
# 示例 :
$function:myFunc.Invoke(114000, 514)    # 輸出 114514

刪除一個一定義的函式, 可以使用 del 語句:

del function:name
# 示例 :
del function:myFunc
echo $function:myFunc     # 由於函式已刪除, 所以不會有任何輸出

函式可以有返回值, 只需要使用 return 語句:

function myFunc($a, $b) { return $a + $b; }
$result = myFunc 114000 514
echo $result      # 輸出 114514

5. 物件導向

- 使用物件

肯定會有人執行這個指令:

echo Hello World!

雖然僅僅少了一對雙引號, 但是, 它表示的意思其實跟之前我們寫的完全不一樣. 如果不加雙引號, 那麼在這個指令中, Hello 和 World! 會被認為是兩個引數! 或者準確來說, 是兩個物件!

這條沒有加雙引號的指令, 輸出結果是:

Hello
World!

另外, 命令的輸出同樣也是一個個物件, 執行下面的指令:

1+1

輸出

2

- .NET 互操作

事實上, PowerShell 中的物件都是 .NET 物件. 並且我們可以進行 .NET 物件中所支援的操作. 執行下面的指令:

(1+1).GetType()

輸出:

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Int32                                    System.ValueType

這熟悉的名稱空間, 可不就是 .NET 嗎?

試試以下指令:

(echo 123).GetType()

輸出:

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Int32                                    System.ValueType

好傢伙, 原來 echo 直接就是把這個物件輸出出來對吧!

而事實上, 一個表示式的運算結果(返回值), 是會列印到控制檯的, 這點與 Python 一樣.

所以如果你執行一個 '1+1', 這個表示式的運算結果是 2, 則 2 會被列印到控制檯.

1+1  # 輸出 2

如果你不希望一個語句的返回值被列印到控制檯, 可以在首部新增 [void], 它表示將語句的返回值強制轉換為 void 以保證沒有輸出

[void](1+1)    # 沒有輸出

使用 New-Object 可以建立一個物件, 指定 .NET 物件的全名即可建立:

$random = New-Object System.Random
$randint = $random.Next()    # 呼叫 System.Random 物件的 Next 方法
echo $randint                # 輸出一個隨機的數字

如果需要帶引數的建立 .NET 物件, 直接在 New-Object 語句後新增引數陣列即可:

$str = New-Object String ('草', 20)    # 生草
echo $str  # 返回 '草草草草草草草草草草草草草草草草草草草草'

PowerShell 中的強制型別轉換是這樣使用的:

[型別]物件
# 示例 :
$numArray = [int[]](1..4)   # 將 object[] 轉換為 int[]

呼叫 .NET 類的靜態方法, 使用下面的語法:

[類的全名]::方法名(引數)
# 示例 :
[Console]::WriteLine("Hello world!")   # 輸出 Hello world

訪問欄位, 也是一樣的

$ansi = [System.Text.Encoding]::Default

如果要使用某個名稱空間, 使用下面的語句:

using namespace System.Text
$ansi = [Encoding]::Default

值得高興的是, 通過呼叫 .NET 的元件, 你甚至可以在 PowerShell 中建立 UI 窗體, 下面是一個簡單的示例程式碼:

Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing

$form = New-Object System.Windows.Forms.Form
$form.Text = 'Select a Computer'
$form.Size = New-Object System.Drawing.Size(300,200)
$form.StartPosition = 'CenterScreen'

$okButton = New-Object System.Windows.Forms.Button
$okButton.Anchor = [System.Windows.Forms.AnchorStyles]::Bottom -bor [System.Windows.Forms.AnchorStyles]::Right
$okButton.Location = New-Object System.Drawing.Point(75,120)
$okButton.Size = New-Object System.Drawing.Size(75,23)
$okButton.Text = 'OK'
$okButton.DialogResult = [System.Windows.Forms.DialogResult]::OK
$form.AcceptButton = $okButton
$form.Controls.Add($okButton)

$cancelButton = New-Object System.Windows.Forms.Button
$cancelButton.Anchor = [System.Windows.Forms.AnchorStyles]::Bottom -bor [System.Windows.Forms.AnchorStyles]::Right
$cancelButton.Location = New-Object System.Drawing.Point(150,120)
$cancelButton.Size = New-Object System.Drawing.Size(75,23)
$cancelButton.Text = 'Cancel'
$cancelButton.DialogResult = [System.Windows.Forms.DialogResult]::Cancel
$form.CancelButton = $cancelButton
$form.Controls.Add($cancelButton)

$label = New-Object System.Windows.Forms.Label
$label.Anchor = [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Left
$label.Location = New-Object System.Drawing.Point(10,20)
$label.Size = New-Object System.Drawing.Size(280,20)
$label.Text = 'Please select a computer:'
$form.Controls.Add($label)

$listBox = New-Object System.Windows.Forms.ListBox
$listBox.Anchor = [System.Windows.Forms.AnchorStyles]::Top -bor [System.Windows.Forms.AnchorStyles]::Left -bor [System.Windows.Forms.AnchorStyles]::Bottom -bor [System.Windows.Forms.AnchorStyles]::Right
$listBox.Location = New-Object System.Drawing.Point(10,40)
$listBox.Size = New-Object System.Drawing.Size(260,20)
$listBox.Height = 80

[void] $listBox.Items.Add('atl-dc-001')
[void] $listBox.Items.Add('atl-dc-002')
[void] $listBox.Items.Add('atl-dc-003')
[void] $listBox.Items.Add('atl-dc-004')
[void] $listBox.Items.Add('atl-dc-005')
[void] $listBox.Items.Add('atl-dc-006')
[void] $listBox.Items.Add('atl-dc-007')

$form.Controls.Add($listBox)

$form.Topmost = $true

$result = $form.ShowDialog()

if ($result -eq [System.Windows.Forms.DialogResult]::OK)
{
    $x = $listBox.SelectedItem
    [System.Windows.Forms.MessageBox]::Show("你選擇了:" + $x.ToString())
} else {
    [System.Windows.Forms.MessageBox]::Show("你沒有選擇任何條目")
}

相關文章