###### tags: `PowerShell`
# PowerShell 學習筆記
## 說明文件
查看本機的說明文件前, 請先更新說明文件:
```ps
Update-Help
```
接著就可以用 `get-help` 觀看本機版的說明文件:
```ps
Get-Help get-item
```
以上看到的是簡易版本的說明, 如果要觀看完整的說明文件, 必須加上 `-Full` 參數:
```ps
Get-Help get-item -Full
```
你也可以查看線上版本的說明文件:
```ps
Get-Help get-item -Online
```
### 指令語法
說明文件中的語法表示方式:
```
SYNTAX
    Move-Item [[-Destination] <System.String>] [-Credential <Sys
    tem.Management.Automation.PSCredential>] [-Exclude <System.S
    tring[]>] [-Filter <System.String>] [-Force] [-Include <Syst
    em.String[]>] -LiteralPath <System.String[]> [-PassThru] [-C
    onfirm] [-WhatIf] [<CommonParameters>]
    Move-Item [-Path] <System.String[]> [[-Destination] <System.
    String>] [-Credential <System.Management.Automation.PSCreden
    tial>] [-Exclude <System.String[]>] [-Filter <System.String>
    ] [-Force] [-Include <System.String[]>] [-PassThru] [-Confir
    m] [-WhatIf] [<CommonParameters>]```
```
1. 你可以看到顯示的語法有兩組, 這表示有兩組參數集, 彼此不能隨意混用。舉例來說, 如果使用了上方參數集獨有的 `-LiteralPath` 參數, 就不能同時使用下方參數集才有的 `-Path` 參數。
3. 每個參數都是 `-參數名稱 參數值` 的形式
4. 沒有被中括號括住的參數就是強制參數, 一定要有, 例如:
    ```
    [-Path] <String[]>
    ```
    
    這個 `Path` 參數就是強制參數, 不過參數名稱有用中括號括起來, 表示指定此參數時, 可以不用加上 `-Path` 指定參數名稱。
    
1. 有用中括號括起來的參數就是可選用的參數, 例如:
    ```
    [-Filter <String>]
    ```
    
    就表示 `Filter` 參數並不一定要有, 需要時才指定。
    
1. 選用參數的參數名稱若有再使用中括號括起來, 稱為**位置**參數, 表示指定該參數時, 並不一定要指定參數名稱, 但若是指令可接受多個位置參數時, 必須依照其在語法說明中的順序出現, 例如:
    
    ```
    [[-Destination] <String>]
    ```
    
    也就是說, 下指令時, 若有多個不具名參數, 就會依照順序一一對應到語法中的個別位置參數。若該指令有強制參數, 那麼第 1 個不具名參數就會對應到強制參數。
    
1. 參數值具有型別, 若型別後面還跟著一對中括號, 表示該參數值可以接受單一資料或是資料清單 (陣列), 例如: 
    ```
    [-Path] <String[]>
    ```
    
    若是資料清單, 清單中的個別項目以逗號相隔, 單一資料內含空白時, 必須以 "" 括起來, 例如:
    
    ```
    Move-Item a.txt,"new file.txt" ..
    ```
    
1. 有些參數只有名稱沒有值, 稱為**開關 (switch)** 參數, 代表 `True` 或是 `False` 的值, 例如:
    ```
    [-Confirm]
    ```
    
    如果查看完整的說明文件, 可以看到這個參數的進一步說明:
    
    ```
    -Confirm [<SwitchParameter>]
            Prompts you for confirmation before running the cmdlet.
            Required?                    false
            Position?                    named
            Default value                False
            Accept pipeline input?       False
            Accept wildcard characters?  false
    ```
    
    就可以知道這個參數不指定的話預設值為 `False`, 指定時代表 `True`。
    
### 指令範例
以下可取得特定指令的範例:
```
get-help Get-Event -Examples
```
## PowerShell 用詞
### command
command 代表各種可以執行的事物, 包括以下幾種:
1. cmdlet:PowerShell (以 .net 撰寫) 原生的指令, 除少數例外, 名稱都是 `動詞-名詞` 的格式, 像是 `Get-Help`。
2. function:用 PowerShell 語言撰寫的指令。
3. application:可執行的應用程式
    
## 語法基礎
### 語法注意要點:
1. PowerShell 並不區分大小寫。
2. 參數名稱最少字母原則, 只要能區分參數名稱, 可以不用打完整的名稱, 例如以下兩者功能相同:
    ```ps
    > Get-Date -DisplayHint time
    下午 05:10:18
    > Get-Date -Disp time
    下午 05:10:27
    ```
    由於沒有其他參數的名稱是 `Disp` 開頭, 因此打 `-Disp` 就可以代表 `-DisplayHint` 參數。
3. 如果指令太長, 可以用[反引號 (backtick, <code>`</code> 符號)](https://devblogs.microsoft.com/scripting/powertip-line-continuation-in-powershell/) 接續下一行, 例如:
    ```ps
    # echo `
    hello
    hello
    ```
### 在指令行中代入指令執行結果
用小括號可以在指令內代入其他指令的執行結果:
```ps
> Write-Host hello (Get-Date)
hello 2020/12/16 下午 04:20:17
```
### 在字串中代入指令執行結果
利用英文雙引號可以在字串中用 `$` 符號代入變數值, 或者用 `$()` 代入指令執行結果:
```ps
❯ echo "time: $(get-date)"
time: 08/13/2021 17:38:22
❯ $name = "John"
❯ echo "hello, $name"
hello, John
```
如果要在字串中表示 `$` 符號, 可以加上 `` ` `` 字元讓 `$` 跳脫置換功能變成一般文字, 或是改用英文單引號來表示字串, 就不會有置換功能了。
## 字串處理
### 規則表達式
PowerSehll 提供有 [`-match`](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_comparison_operators?view=powershell-7.1#-match-and--notmatch) 及 [`-replace`](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_comparison_operators?view=powershell-7.1#replacement-with-regular-expressions) 運算器, 可以依據[規則表達式](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_regular_expressions?view=powershell-7.1)比對或是取代字串, 例如:
```ps
❯ "john_at_gmail.com" -match "((.+)@(.+))"
False
```
如果比對成功, 它會自動產生一個雜湊陣列 `$Matches`, 包含比對相符的內容:
```ps
❯ "john@gmail.com" -match "((.+)@(.+))"
True
❯ $Matches
Name                           Value
----                           -----
3                              gmail.com
2                              john
1                              john@gmail.com
0                              john@gmail.com
```
索引鍵 '0' 為相符的子字串內容, 索引鍵 '1' 開始是從最外層依序到最裡層的群組相符的子字串內容。
`-nomatch` 則是和 `-match` 相反的運算器。
你也可以處理字串替換, 例如:
```ps
❯ "john@gmail.com" -replace "((.+)@(.+))",'$2_at_$3'
john_at_gmail.com
```
注意在 PowerShell 中英文雙引號的字串會進行變數置換, 所以在使用規則表達式取代字串時, 最好用英文單引號, 避免以 `$` 字元標示的群組編號被當成變數。
## Provider
PowerShell 使用 Provider 抽象化系統內儲存的資料, 例如檔案系統、登錄檔、甚至環境變數等等。
以下指令可以得知目前系統上有哪些 Provider:
```ps
> Get-PSProvider
Name                 Capabilities                                  Drives
----                 ------------                                  ------
Registry             ShouldProcess                                 {HKLM, HKCU}
Alias                ShouldProcess                                 {Alias}
Environment          ShouldProcess                                 {Env}
FileSystem           Filter, ShouldProcess, Credentials            {C, D, Temp, E…}
Function             ShouldProcess                                 {Function}
Variable             ShouldProcess                                 {Variable}
```
### 列出抽象的磁碟
以下指令則可以知道系統上有哪些 (含抽象的) 磁碟機:
```
> Get-PSDrive
Name           Used (GB)     Free (GB) Provider      Root                                        CurrentLocation
----           ---------     --------- --------      ----                                        ---------------
Alias                                  Alias
C                  85.68          8.88 FileSystem    C:\                                           Users\ShinWei
Cert                                   Certificate   \
D                  95.20         32.66 FileSystem    D:\                                                    temp
E                   3.98          0.00 FileSystem    E:\
Env                                    Environment
F                                      FileSystem    F:\
Function                               Function
HKCU                                   Registry      HKEY_CURRENT_USER
HKLM                                   Registry      HKEY_LOCAL_MACHINE
M                 258.26        596.53 FileSystem    \\192.168.0.244\maker_disk
Temp               85.68          8.88 FileSystem    C:\Users\ShinWei\AppData\Local\Tem…
Variable                               Variable
WSMan                                  WSMan
```
### 存取環境變數
你可以像是操作檔案系統的方式操作這些資料, 例如檢視環境變數:
```ps
> cd Env:
> Get-Item temp
Name                           Value
----                           -----
TEMP                           C:\Users\ShinWei\AppData\Local\Temp
> Get-Content temp
C:\Users\ShinWei\AppData\Local\Temp
```
### 存取登錄檔
也可以這樣操作登錄檔:
```ps
> cd HKLM:
> Set-Location .\SOFTWARE\
> cd .\Windows\
> ls
    Hive: HKEY_LOCAL_MACHINE\SOFTWARE\Windows
Name                           Property
----                           --------
CurrentVersion
```
## 所有資料都是物件
取得集合中物件的屬性清單:
```ps
>Get-Date
2023年6月23日 下午 03:01:37
>Get-Date | Get-Member
  TypeName: System.DateTime
Name                 MemberType     Definition
----                 ----------     ----------
Add                  Method         datetime Add(timespan value)
AddDays              Method         datetime 
...
Year                 Property       int Year {get;}
DateTime             ScriptProperty System.Object DateTime {get=…
```
使用 `year` 取得年份:
```ps
> (get-date).year
2023
```
所有的指令都是針對物件在操作:
```ps
> Get-Item *.html
    Directory: D:\temp
Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a---        2020/12/15 下午 06:23           1010 index.html
-a---        2020/12/17 上午 09:49          24109 opencv.html
```
我們可以針對兩個檔案物件排序:
```ps
> (Get-Item *.html) | Sort-Object -Property Length -Descending
    Directory: D:\temp
Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a---        2020/12/17 上午 09:49          24109 opencv.html
-a---        2020/12/15 下午 06:23           1010 index.html
```
取得物件的屬性:
```ps
> get-process | Get-Member
   TypeName: System.Diagnostics.Process
Name                       MemberType     Definition
----                       ----------     ----------
Handles                    AliasProperty  Handles = Handlecount
Name                       AliasProperty  Name = ProcessName
NPM                        AliasProperty  NPM = NonpagedSystemMemorySize64
...
```
篩選物件的屬性, 只留下指定的屬性 (移除未指定屬性):
```
> get-process | Select-Object -Property Name | Get-Member
   TypeName: Selected.System.Diagnostics.Process
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=ACMON
```
用指定的屬性替代原始的物件:
```
> get-process | Select-Object -ExpandProperty Name | Get-Member
   TypeName: System.String
Name                 MemberType            Definition
----                 ----------            ----------
Clone                Method                System.Object Clone(), System.Object ICloneable.Clone()
...
```
若該屬性是陣列, 就會展開陣列的內容, 例如:
```
> get-process -Name explorer | Select-Object -ExpandProperty Modules | Select-Object -Property ModuleName
ModuleName
----------
Explorer.EXE
ntdll.dll
...
EFSUTIL.dll
DSROLE.dll
Windows.Internal.System.UserProfile.dll
CloudExperienceHostBroker.dll
```
這一堆 .dll 就是 Modules 屬性展開後的每一個 Module。
## 物件預設的顯示格式
請參考[這裡](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_format.ps1xml?view=powershell-7.1)
## PowerShell 的系統變數
### $PROFILE
$PROFILE 變數會記錄目前使用的 PowerSehll 設定檔路徑:
```
❯ $PROFILE
C:\Users\ShinWei\Documents\PowerShell\Microsoft.PowerShell_profile.ps1
```
### $PSVersionTable
$PSVersionTable 可以取得 PowerShell 版本資訊:
```
❯ $PSVersionTable
Name                           Value
----                           -----
PSVersion                      7.1.3
PSEdition                      Core
GitCommitId                    7.1.3
OS                             Microsoft Windows 10.0.22000
Platform                       Win32NT
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0
```
## 腳本區塊 (Script Block)
[腳本區塊](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_script_blocks?view=powershell-7.1)是一種功能類似匿名函式的物件, 定義的方式就是用大括號將要執行的內容包起來, 例如:
```ps
❯ $s = { echo "hello" }
```
要叫用定義好的腳本區塊, 必須使用 [`&`](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_operators?view=powershell-7.1#call-operator-) 運算器:
```ps
❯ & $s
hello
```
你也可以幫腳本區塊定義引數, 例如:
```ps
❯ $s = {
>> param(
>>   [String]$name
>> )
>> echo "hello, $name"
>> }
```
叫用時可以利用位置傳遞傳數:
```ps
❯ & $s "John"
hello, John
```
也可以指名方式傳遞參數:
```ps
❯ & $s -name "John"
hello, John
```
### 從字串建立並執行腳本區塊
如果要從字串中記錄的敘述建立並執行腳本區塊, 可以搭配 [`Invoke-Expression`](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/invoke-expression?view=powershell-7.1), 例如若我們讀取一個腳本檔案, 並將內容記錄在變數中:
```ps
❯ $s
param([String]$name) echo "hello, $name"
```
如果想使用 `$s` 的內容建構成腳本執行, 你可能會想這樣做:
```ps
❯ $err = { $s }
```
但這樣建構出來的腳本區塊在叫用時, 是執行 `$s`, 所以只會顯示字串的內容:
```ps
❯ & $err -name Mary
param([String]$name) echo "hello, $name"
```
正確的作法是先透過字串置換, 然後再使用 `Invoke-Expression` 來執行叫用腳本區塊:
```ps
❯ $correct = "& { $s } -name 'Mary'"
❯ Invoke-Expression $correct
hello, Mary
```
你也可以省略中間置換字串的變數, 直接寫成一列:
```ps
❯ Invoke-Expression "& { $s } -name 'Mary'"
hello, Mary
```
在底下的[更新 PowerShell Core 到最新版](#%E6%9B%B4%E6%96%B0-PowerShell-Core-%E5%88%B0%E6%9C%80%E6%96%B0%E7%89%88)中, 就有實際運用的範例, 會從網路下載的腳本檔建立腳本區塊執行。
## 重新導向與管線
### 用 ForEach-Object 一行一行取得程式輸出做處理
單純用管線取得程式輸出的話, 會等到程式結束才傳回所有的輸出內容, 如果希望一行一行取得輸出結果處理, 不用等到程式結束, 可以使用 [`ForEach-Object`](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/foreach-object?view=powershell-7.1):
```
❯ ping -t 8.8.8.8 | ForEach-Object -process { $_ -replace '時間', 'time' }
Ping 8.8.8.8 (使用 32 位元組的資料):
回覆自 8.8.8.8: 位元組=32 time=6ms TTL=115
回覆自 8.8.8.8: 位元組=32 time=7ms TTL=115
回覆自 8.8.8.8: 位元組=32 time=5ms TTL=115
回覆自 8.8.8.8: 位元組=32 time=5ms TTL=115
```
其中 `-process` 是預設參數, 所以可以省略不寫:
```
❯ ping -t 8.8.8.8 | ForEach-Object { $_ -replace '時間', 'time' }
Ping 8.8.8.8 (使用 32 位元組的資料):
回覆自 8.8.8.8: 位元組=32 time=11ms TTL=115
回覆自 8.8.8.8: 位元組=32 time=8ms TTL=115
回覆自 8.8.8.8: 位元組=32 time=9ms TTL=115```
```
另外, `%` 與 `foreach` 是 `ForEach-Object` 的別名, 可以縮短指令行:
```
❯ ping -t 8.8.8.8 | % { $_ -replace '時間', 'time' }
Ping 8.8.8.8 (使用 32 位元組的資料):
回覆自 8.8.8.8: 位元組=32 time=7ms TTL=115
回覆自 8.8.8.8: 位元組=32 time=4ms TTL=115
回覆自 8.8.8.8: 位元組=32 time=8ms TTL=115
```
:::danger
如果 foreach 出現在一列的開頭, 會變成 [`foreach`](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_foreach?view=powershell-7.1&source=docs) 敘述, 雖然和上面談的 `ForEach-Object` 內建指令相似, 但它無法一行一行取得程式輸出, 只能接收完整的集合資料後才一行行處理。例如:
```
❯ foreach($i in (ping 8.8.8.8)) {get-date -disp time; echo $i}
下午 01:33:44
下午 01:33:44
Ping 8.8.8.8 (使用 32 位元組的資料):
下午 01:33:44
回覆自 8.8.8.8: 位元組=32 時間=5ms TTL=115
下午 01:33:44
回覆自 8.8.8.8: 位元組=32 時間=4ms TTL=115
下午 01:33:44
回覆自 8.8.8.8: 位元組=32 時間=8ms TTL=115
下午 01:33:44
回覆自 8.8.8.8: 位元組=32 時間=4ms TTL=115
下午 01:33:44
下午 01:33:44
8.8.8.8 的 Ping 統計資料:
下午 01:33:44
    封包: 已傳送 = 4,已收到 = 4, 已遺失 = 0 (0% 遺失),
下午 01:33:44
大約的來回時間 (毫秒):
下午 01:33:44
    最小值 = 4ms,最大值 = 8ms,平均 = 5ms
```
可以看到時間都在同一秒, 但若是改用 `ForEach-Object`:
```
❯ ping 8.8.8.8 | % { Get-Date -disp time; echo $_ }
下午 01:37:18
下午 01:37:18
Ping 8.8.8.8 (使用 32 位元組的資料):
下午 01:37:18
回覆自 8.8.8.8: 位元組=32 時間=7ms TTL=115
下午 01:37:19
回覆自 8.8.8.8: 位元組=32 時間=11ms TTL=115
下午 01:37:20
回覆自 8.8.8.8: 位元組=32 時間=10ms TTL=115
下午 01:37:21
回覆自 8.8.8.8: 位元組=32 時間=9ms TTL=115
下午 01:37:21
下午 01:37:21
8.8.8.8 的 Ping 統計資料:
下午 01:37:21
    封包: 已傳送 = 4,已收到 = 4, 已遺失 = 0 (0% 遺失),
下午 01:37:21
大約的來回時間 (毫秒):
下午 01:37:21
    最小值 = 7ms,最大值 = 11ms,平均 = 9ms
```
就會看到 `ping` 每一秒動作一次產生輸出後, 就會立刻經由 `ForEach-Object` 的腳本區塊處理, 所以中間的幾行訊息時間間隔都是 1 秒鐘。
兩者的差異可以參考[這一篇文章](https://devblogs.microsoft.com/scripting/getting-to-know-foreach-and-foreach-object/)。
:::
## PowerShell 範例
以下蒐集一些小範例供參考使用。
### 以管理員權限執行指令
若要像是 Linux 下以 sudo 指令提升成管理員權限執行指令, 可以使用 `Start-Process` 搭配 `-verb runas` 參數執行指令, 例如:
```
❯ start-process netsh -verb runas
```
就會以管理員權限執行 netsh 指令, 過程中還是會詢問是否允許:

只要按**是**即可。
:::info
也可以使用 runas 指令, 例如:
```
❯ runas /user:mee\shinwei netsh
輸入 mee\shinwei 的密碼:
嘗試以使用者 "mee\shinwei" 的身分啟動 netsh ...
```
:::
### 計算執行時間
PowerShell 本身沒有計時的 cmdlet, 不過可以利用 .net 的 StopWatch 物件來計時, 首先產生物件:
```
❯ $sw = New-Object -TypeName System.Diagnostics.Stopwatch
```
接著即可使用 Start()、Stop()、Reset() 方法操控計時器, 並使用 Elapsed 屬性取得目前累計的計時時間。例如:
```
❯ $sw.Reset();$sw.Start();sleep 3;$sw.Stop()
❯ $sw.Elapsed
Days              : 0
Hours             : 0
Minutes           : 0
Seconds           : 3
Milliseconds      : 13
Ticks             : 30138681
TotalDays         : 3.48827326388889E-05
TotalHours        : 0.000837185583333333
TotalMinutes      : 0.050231135
TotalSeconds      : 3.0138681
TotalMilliseconds : 3013.8681
```
### 搜尋字串
#### `Select-String`
要篩選輸出內容, 可以使用 [`Select-String`](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/select-string?view=powershell-7.3) 搜尋文字或是規則表達式, 例如有一個這樣的文字檔:
```ps
❯ type .\test.txt
hello
this
is
a
book
```
以下的指令就可以篩選出有 "this" 的那一行:
```ps
# cat test.txt | Select-String 'this'
this
```
要注意的是, `Select-String` 是針對文字物件搜尋, 如果是要篩選 cmdlet 輸出的物件, 必須先用 [`Out-string`](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/out-string?view=powershell-7.3) 轉成文字物件再篩選:
```ps
# alias | Out-String -stream | Select-String 'Get-Content'
Alias           cat -> Get-Content
Alias           gc -> Get-Content
Alias           type -> Get-Content
```
要特別留意 `Out-String` 的 `-stream` 參數, 如果沒有加上此參數, 就會轉換成一個包含多行文字的字串物件, 這樣搜尋到字串時, 顯示的是多行的文字。加上 `-stream` 選項會轉換成多個單行文字的字串物件, 就只會顯示搜尋到文字的那一行。
#### `find.exe`
在 PowerShell 中如果要使用 find 搜尋檔案中的字串, 會因為 PowerShell 把雙引號括起來的內容當成字串物件, 使得實際傳送給 find 的參數內容沒有雙引號而出錯
使用 find 會出錯:
```ps
❯ find "this" test.txt
FIND: 參數格式不正確
```
必須用單引號把雙引號包起來, 改用 '"要搜尋的文字"' 才能將含有雙引號的文字當成參數傳給 find:
```
❯ find '"this"' test.txt
---------- TEST.TXT
this
```
#### `findstr.exe`
如果覺得太麻煩, 也可以改用 findstr 指令, 不需要引號:
```
❯ findstr this test.txt
this
```
#### 找尋 gcc 特定表頭檔內的定義內容
以下是找尋 `time.h` 檔案中名稱含有 "time" 的自訂資料型別:
```
echo "" | gcc -E -xc -include 'time.h' - | Select-String -Pattern "typedef" | Select-String -Pattern "time" 
```
有關 gcc 選項說明如下:
1. `-E` 會在前置處理完就停止。
2. 最後指定 `-` 當檔名表示不是從檔案讀取原始碼, 而是從標準輸入讀取, 這裡因為有資料通道的轉向, 原始碼就是前面的 `echo` 送出的空字串。
3. `-x` 是指定要編譯哪一種程式語言, 此處 `-xc` 表示是 C 語言。由於是從標準輸入讀取原始碼, 所以 gcc 無法依據副檔名判斷程式語言種類, 這裡一定要指定程式語言才能正常運作。
4. `-include` 可以指定要匯入的表頭檔, 這裡就是將 `time.h` 匯入。
以上的 gcc 指令實際結果就是讓前置處理器把 `time.h` 讀入與空的原始碼合併後輸出, 也就可以看到 `time.h` 檔案的內容。
後面透過資料通道串接的 `Select-String` 可以搜尋文字。
:::info
這個方式找不到像是 `#define` 定義的項目, 因為在前置處理器處理後, 這些項目都已經被取代掉了。
:::
### 計算資料夾大小
```
# Get-ChildItem .\get-pip.py | Measure-Object -Property Length -sum
Count             : 1
Average           :
Sum               : 2658865
Maximum           :
Minimum           :
StandardDeviation :
Property          : Length
```
### 更新 PowerShell Core 到最新版
要在 PowerShell 內更新 PowerShell, 可以使用以下[指令](https://github.com/PowerShell/PowerShell/blob/master/tools/install-powershell.ps1-README.md):
```bash
iex "& { $(irm https://aka.ms/install-powershell.ps1) } -UseMSI"
```
- irm 是 [Invoke-RestMethod](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/invoke-restmethod?view=powershell-7.1) 這個 cmdlet 的別名, 可以送出 http 請求, 用來取得結構性的資料。本例就是用它來下載 [PowerShell 的安裝腳本](https://aka.ms/install-powershell.ps1)。
- [-UseMSI](https://github.com/PowerShell/PowerShell/blob/1ec61d955c5f7e1e62a83d6a0d1dab3882bd73c9/tools/install-powershell.ps1#L42) 是安裝腳本的選項, 表示下載 MSI 格式的安裝檔執行安裝程序。
- iex 是 [Invode-Expression](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/invoke-expression?view=powershell-7.1) 這個 cmdlet 的別名, 可以執行字串參數內的指令。本例就是執行後面參數字串中叫用[腳本區塊](#%E8%85%B3%E6%9C%AC%E5%8D%80%E5%A1%8A-Script-Block)的動作。
## Module
以下指令可以得知模組的安裝位置:
```
> Get-Content Env:\PSModulePath
C:\Users\ShinWei\Documents\PowerShell\Modules;C:\Program Files\PowerShell\Modules;c:\program files\powershell\7\Modules;C:\Program Files\WindowsPowerShell\Modules;C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules
```
以下指令可以列出已載入的模組:
```
> Get-Module
ModuleType Version    PreRelease Name
---------- -------    ---------- ----
Manifest   1.0.0.0               DnsClient
Manifest   7.0.0.0               Microsoft.PowerShell.Management
Manifest   7.0.0.0               Microsoft.PowerShell.Security
Manifest   7.0.0.0               Microsoft.PowerShell.Utility
Manifest   7.0.0.0               Microsoft.WSMan.Management
Script     2.0.399               oh-my-posh
Script     1.0.0      beta4      posh-git
Script     2.1.0                 PSReadLine
```
搜尋網路上可安裝的模組:
```
> Find-Module posh-git
Version              Name                                Reposito
                                                         ry
-------              ----                                --------
0.7.3                posh-git                            PSGalle…
```
若要包含未正式發佈的版本:
```
> Find-Module posh-git -AllowPrerelease
Version              Name                                Reposito
                                                         ry
-------              ----                                --------
1.0.0-beta4          posh-git                            PSGalle…
```
已安裝的模組會在執行到其中的指令時自動載入, 例如若先卸載已載入的模組, 然後執行 oh-my-posh 模組的 Set-Theme 指令, 就會自動載入 oh-my-posh 模組:
```
> Get-Module | Remove-Module
> Get-Module
ModuleType Version    PreRelease Name
---------- -------    ---------- ----
Manifest   7.0.0.0               Microsoft.PowerShell.Management
Manifest   7.0.0.0               Microsoft.PowerShell.Utility
Script     1.4.7                 PackageManagement
> Set-Theme Agnoster
> Get-Module
ModuleType Version    PreRelease Name
---------- -------    ---------- ----
Manifest   7.0.0.0               Microsoft.PowerShell.Management
Manifest   7.0.0.0               Microsoft.PowerShell.Utility
Script     2.0.399               oh-my-posh
Script     1.4.7                 PackageManagement
Script     1.0.0      beta4      posh-git
```