iT邦幫忙

2024 iThome 鐵人賽

DAY 20
0
DevOps

《30天挑戰精通 PowerShell:從 Windows Server 到 Azure DevOps 自動化之旅》系列 第 20

Day 20 - 互動指令碼的原理:Read-Host、Write-Host、Write-Output

  • 分享至 

  • xImage
  •  

對應 30天挑戰精通 PowerShell 該書第 17 章「輸出與輸入」。

本章的內容僅適用於與人類的視覺和觸覺進行互動的指令碼。對於自動執行且無需人工干預( unattended )的指令碼而言,這些技巧並不適合,因為不會有人在場與之互動。
Reference - P.275

雖說上述的解釋,似乎跟自動化扯不上邊,但是當我看到章節標題裡的 Write-Host 及 Write-Output 時,我心中還是跳出了疑惑,對耶,這兩個差在哪?讓我繼續看下去。


提示和顯示資訊

書中逐步解釋了 PowerShell 中的顯示和提示機制,並針對不同的環境提供具體說明,首先提到了 PowerShell 引擎以及主機應用程式這兩個名詞:

PowerShell 引擎

是實際執行 PowerShell 語法並生成結果的核心部分,你可以想像他就是一個強大的機器,它能夠執行各種命令並生成結果。

主機應用程式( host application )

是你與 PowerShell 引擎進行互動的工具,它負責接收輸入(你的命令)以及顯示輸出(命令執行後的結果),把它想像成是控制這台機器的面板,你在面板上按的每一個按鈕(輸入命令),面板會展示執行後的結果(顯示輸出)。
而書中提出了三個不同類型的主機應用程式並解釋到:

主控台主機(ConsoleHost)

  • 這是你在本地終端(如 Windows PowerShell、PowerShell Core)中常見的應用程式。
  • 當你在終端中執行命令時,輸入和輸出會直接顯示在終端視窗中。

整合式主機(Integrated Host)

  • 像 Visual Studio Code 中的 PowerShell 擴充套件,它提供了一個更圖形化的界面,讓你同時編寫和執行腳本。
  • 這種環境下,執行結果會顯示在內建的 PowerShell 終端,而不是本地終端。

雲端主機(例如 Azure Functions)

  • 在這種環境下,PowerShell 可能會被託管在一個雲端服務中,並且輸出會被記錄到日誌中,而不是直接顯示在你本地的終端上。

而了解不同主機應用程式如何處理 PowerShell 的輸入和輸出,能夠幫助我們在不同的環境中正確預期命令的行為,避免混淆。例如:當你發現同樣的命令在本地執行和在 Azure 中的行為不同時,你會知道這是因為兩個不同的主機應用程式在工作,而不是命令本身有問題。


Read-Host

Read-Host cmdlet 用於從用戶那裡獲取輸入。它會在控制台上顯示一個提示( text prompt ),然後等待用戶輸入,並將輸入作為字符串返回,而下面的範例都把「獲得的輸入」存到一個變數中:

Examples

# --------- Example 1: Save console input to a variable ---------

$Age = Read-Host "Please enter your age"


# ------- Example 2: Save console input as a secure string -------

$pwd_secure_string = Read-Host "Enter a Password" -AsSecureString


# ------- Example 3: Mask input and as a plaintext string -------

$pwd_string = Read-Host "Enter a Password" -MaskInput

Case - 還原 Example 2 的密碼

僅有 Windows 能夠執行該動作,像我用 macOS 嘗試還原它會因為SecureString 的功能在 macOS 上受限,無法像在 Windows 上那樣運作。

$pwd_secure_string = Read-Host "Enter a Password" -AsSecureString

$plain_password = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto(
    [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($pwd_secure_string)
)

# Print the plain text password to confirm the result
Write-Output $plain_password

Write-Host

先來看看書中的解釋:

Write-Host 就像其他的 cmdlet 一樣在管線中執行,但它不會將任何內容放入管線內。它實際上做了兩件事情:寫入一筆紀錄到「資料流」( Information stream )中,並且直接寫入到主機應用程式的畫面上。
https://ithelp.ithome.com.tw/upload/images/20241004/201687087Cf6GDOjlz.png
Reference - P.277 ~ P.278

而對我來說,Write-Host 用於將輸出直接寫到控制台(主機應用程式),它可以設置文本的顏色和背景色,非常適合用於顯示需要強調的信息,而書中也特別強調,Write-Host不是產生錯誤訊息、告警、除錯訊息等等的最佳方式,而建議我們可以改用 Write-Verbose 來處理,其原因是輸出結果會改傳到「詳細資訊流」( verbose stream )。
直到這,已經有兩個關鍵術語是我聽不懂的,資訊流( Information stream )及詳細資訊流( verbose stream ),不過沒關係,我先依循書中的章節規劃,如果看完這章節都沒有解釋,讓我來整理並彙整其解釋。
先讓我介紹一下使用 Write-Host 時,三個好用的參數:

BackgroundColor

指定其背景顏色

PS /Users/kanglin> get-help write-host -Parameter BackgroundColor

-BackgroundColor <System.ConsoleColor>
    Specifies the background color. There is no default. The acceptable values for this parameter are:
    - `Black`
    - `DarkBlue`
    - `DarkGreen`
    - `DarkCyan`
    - `DarkRed`
    - `DarkMagenta`
    - `DarkYellow`
    - `Gray`
    - `DarkGray`
    - `Blue`
    - `Green`
    - `Cyan`
    - `Red`
    - `Magenta`
    - `Yellow`
    - `White`

    Required?                    false
    Position?                    named
    Default value                None
    Accept pipeline input?       False
    Accept wildcard characters?  false

ForegroundColor

指定文字顏色

PS /Users/kanglin> get-help write-host -Parameter ForegroundColor

-ForegroundColor <System.ConsoleColor>
    Specifies the text color. There is no default. The acceptable values for this parameter are:
    - `Black`
    - `DarkBlue`
    - `DarkGreen`
    - `DarkCyan`
    - `DarkRed`
    - `DarkMagenta`
    - `DarkYellow`
    - `Gray`
    - `DarkGray`
    - `Blue`
    - `Green`
    - `Cyan`
    - `Red`
    - `Magenta`
    - `Yellow`
    - `White`

    Required?                    false
    Position?                    named
    Default value                None
    Accept pipeline input?       False
    Accept wildcard characters?  false

Separator

指定要在主機顯示的物件之間插入的分隔符號字串

PS /Users/kanglin> get-help write-host -Parameter Separator

-Separator <System.Object>
    Specifies a separator string to insert between objects displayed by the host.

    Required?                    false
    Position?                    named
    Default value                None
    Accept pipeline input?       False
    Accept wildcard characters?  false

範例

Write-Host (2,4,6,8,10,12) -Separator ", -> " -ForegroundColor DarkGreen -BackgroundColor White

https://ithelp.ithome.com.tw/upload/images/20241004/20168708gMMgLOY6FE.png


Write-Output

先來看看書中的解釋:

不同於 Write-Host,Write-Output 可以將物件傳送進管線。因為他不是直接寫入到顯示器上,因此它無法讓你指定不同的顏色或其他任何設定。事實上,Write-Output(或是它的別名 Write )本質上不是用來顯示輸出結果的,正如我們所說,它將物件傳送進管線 — 最終是管線自己要負責顯示這些物件。下圖展示了運作的流程:
https://ithelp.ithome.com.tw/upload/images/20241004/20168708qjdPXTLCLK.png
*Reference - P.280 *

看到這裡,我自己理解後的解釋是:Write-Output 將對象輸出到管道。而我們可以用另外的命令(例如 Where-Object )將其重新 Filter 以影響最終呈現在終端機(主機應用程式)上的結果或將其賦值給變數(雖然我感受不到在什麼情況下會需要透過 Write-Output 去賦值)。


總結兩者的差別

Write-Host 直接將輸出寫到終端機(主機應用程式),不參與管線,也不能被重定向或捕獲。
Write-Output 將輸出發送到管線,可以被下一個命令接收,或者被重定向或捕獲。

對比範例

# 使用 Write-Host
$result = Write-Host "Hello, World!"
Write-Host $result # $result 將為 $null,因為輸出不能被捕獲


# 使用 Write-Output
$result = Write-Output "Hello, World!"
Write-Host $result # $result 將包含字符串 "Hello, World!"

https://ithelp.ithome.com.tw/upload/images/20241004/20168708eOzTfPsnyM.png


其他輸出方式

書中提供了 Write 字眼的表格,我這邊透過 ChatGPT 整理了一份我認為更清楚的表格作為今天這章節的結尾:

PowerShell 輸出方法總覽

Cmdlet 名稱 說明 相關變數設定 預設值 可能的選項
Write-Host 在控制台上顯示訊息,不傳遞到管道
Write-Output 將物件寫入輸出管道,可被後續的管道命令接收
Write-Error 將錯誤訊息寫入錯誤管道,會引發錯誤事件 $ErrorActionPreference Continue ContinueStopSilentlyContinueInquire
Write-Warning 將警告訊息寫入警告管道 $WarningPreference Continue ContinueSilentlyContinueStopInquire
Write-Verbose 輸出詳細訊息,通常用於提供腳本運行的詳細資訊 $VerbosePreference SilentlyContinue SilentlyContinueContinueStopInquire
Write-Debug 輸出除錯訊息,用於腳本除錯階段 $DebugPreference SilentlyContinue SilentlyContinueContinueStopInquire
Write-Information 將資訊訊息寫入資訊管道,可被特定偏好設定控制 $InformationPreference SilentlyContinue SilentlyContinueContinueStopInquire
Write-Progress 顯示進度條,提供操作進度的視覺化反饋

使用示例

Write-Warning

$WarningPreference = "Continue"
Write-Warning "這是一條警告訊息。"

Write-Verbose

$VerbosePreference = "Continue"
Write-Verbose "這是一條詳細訊息。"

Write-Debug

$DebugPreference = "Continue"
Write-Debug "這是一條除錯訊息。"

Write-Information

$InformationPreference = "Continue"
Write-Information "這是一條資訊訊息。"

Write-Progress

for ($i = 0; $i -le 100; $i += 10) {
    Write-Progress -Activity "正在處理" -Status "進度:$i%" -PercentComplete $i
    Start-Sleep -Milliseconds 500
}

明日主題

Day 21 - 工作階段:更輕鬆的遠端控制


上一篇
Day 19 - 變數:存放資料的地方
下一篇
Day 21 - 工作階段:更輕鬆的遠端控制
系列文
《30天挑戰精通 PowerShell:從 Windows Server 到 Azure DevOps 自動化之旅》30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言