iT邦幫忙

2024 iThome 鐵人賽

DAY 7
1

接續昨天的管線:串接命令,今天主要紀錄

  • 透過 PowerShell 操作命令後,將其物件匯出成 JSON、XML 類型的檔案。
  • 匯入 JSON、XML 檔案成物件。
  • Cmdlet:Compare-Object。

操作 JSON

透過 Get-Command 先搜尋包含 JSON 的 command

PS /Users/kanglin/code/30day> get-command -Noun *json* | Format-Table -AutoSize

CommandType Name             Version Source
----------- ----             ------- ------
Cmdlet      ConvertFrom-Json 7.0.0.0 Microsoft.PowerShell.Utility
Cmdlet      ConvertTo-Json   7.0.0.0 Microsoft.PowerShell.Utility
Cmdlet      Test-Json        7.0.0.0 Microsoft.PowerShell.Utility

匯出

透過 Get-Help 確認 ConvertTo-Json 的用法

PS /Users/kanglin/code/30day> get-help ConvertTo-Json

NAME
    ConvertTo-Json

SYNOPSIS
    Converts an object to a JSON-formatted string.

SYNTAX
    ConvertTo-Json [-InputObject] <System.Object> [-AsArray] [-Compress] [-Depth <System.Int32>] [-EnumsAsStrings] [-EscapeHandling <Newtonsoft.Json.StringEscapeHandling>] [<CommonParameters>]

將 cmdlet 執行的結果存成 Json 檔案

第一層命令 Get-Item 讀取資料夾 30days。

PS /Users/kanglin/code/30days> Get-Item ../30days/

    Directory: /Users/kanglin/code

UnixMode         User Group         LastWriteTime         Size Name
--------         ---- -----         -------------         ---- ----
drwxr-xr-x    kanglin staff       2024/9/21 11:22           64 30days

透過 pipeline 接第二層命令 ConvertTo-Json,將 Get-Item 的結果轉成 JSON 格式並呈現在終端機上。( 僅截圖部分 output )。

Get-Item ../30days/ | ConvertTo-Json | more
WARNING: Resulting JSON is truncated as serialization has exceeded the set depth of 2.
{
  "Name": "30days",
  "Parent": {
    "Name": "code",
    "Parent": {
      "Name": "kanglin",
      "Parent": "/Users",
      "Root": "/",
      "Exists": true,
      "FullName": "/Users/kanglin",
      "Extension": "",
      "CreationTime": "2023-09-17T22:22:28.1711557+08:00",
      "CreationTimeUtc": "2023-09-17T14:22:28.1711557Z",
      "LastAccessTime": "2024-09-21T11:26:39.6835802+08:00",
      "LastAccessTimeUtc": "2024-09-21T03:26:39.6835802Z",
      "LastWriteTime": "2024-09-21T11:26:39.6681179+08:00",
      "LastWriteTimeUtc": "2024-09-21T03:26:39.6681179Z",
      "LinkTarget": null,
      "UnixFileMode": 488,
      "Attributes": 16
    },
    "Root": {
      "Name": "/",
      "Parent": null,
      "Root": "/",
      "Exists": true,
      "FullName": "/",
      "Extension": "",
      "CreationTime": "2024-08-04T18:31:41+08:00",
      "CreationTimeUtc": "2024-08-04T10:31:41Z",
      "LastAccessTime": "2024-08-04T18:31:41+08:00",
      "LastAccessTimeUtc": "2024-08-04T10:31:41Z",
      "LastWriteTime": "2024-08-04T18:31:41+08:00",
      "LastWriteTimeUtc": "2024-08-04T10:31:41Z",
      "LinkTarget": null,
      "UnixFileMode": 493,
      "Attributes": 17
    },
    "Exists": true,
    "FullName": "/Users/kanglin/code",
    "Extension": "",

再補上一個 pipeline 用第三個命令 Out-File 將剛剛第二層指令所產生的 JSON 格式內容存成 JSON file,在產生時,會跳 Warning 顯示物件內容因其巢狀屬性太多階層,超過第二層被截斷(預設到第二層)。

PS /Users/kanglin/code/30days> Get-Item ../30days/ | ConvertTo-Json | Out-File DisplayFolderPropertiesDepth2.json
WARNING: Resulting JSON is truncated as serialization has exceeded the set depth of 2.

因為看到這樣的訊息,我就在想,到底可以到第幾層,因此透過 ConvertTo-Json 的參數去控制 Depth 的數值,到了第五層時,簡短的 Get-Item 指令居然可以產出近 53M 大小的 JSON file。所以我想除非任務上有需求,不然用 Default 的階層即可。
https://ithelp.ithome.com.tw/upload/images/20240921/20168708KoXpePevPJ.png

匯入

透過 Get-Help 確認 ConvertTo-Json 的用法

PS /Users/kanglin/code/30days> Get-Help ConvertFrom-Json

NAME
    ConvertFrom-Json

SYNOPSIS
    Converts a JSON-formatted string to a custom object or a hash table.

SYNTAX
    ConvertFrom-Json [-InputObject] <System.String> [-AsHashtable] [-Depth <System.Int32>] [-NoEnumerate] [<CommonParameters>]

將 JSON file 匯入成物件

但是原先透過 Get-Item 所轉成的 Json file,再匯回物件時,所呈現出來的 table 並不是原先的格式,不過仔細看內容,可以發現原先的值是有被正確保存的,只是需要另外再做處理,這邊就不再往下延伸。

PS /Users/kanglin/code/30days> Get-Content ./DisplayFolderPropertiesDepth2.json | ConvertFrom-Json

https://ithelp.ithome.com.tw/upload/images/20240921/20168708Qa28zR7l9B.png


操作 XML

透過 Get-Command 先搜尋包含 XML 的 command

PS /Users/kanglin/code/30days> get-command -Noun *xml* | Format-Table -AutoSize

CommandType Name          Version Source
----------- ----          ------- ------
Cmdlet      ConvertTo-Xml 7.0.0.0 Microsoft.PowerShell.Utility
Cmdlet      Export-Clixml 7.0.0.0 Microsoft.PowerShell.Utility
Cmdlet      Import-Clixml 7.0.0.0 Microsoft.PowerShell.Utility
Cmdlet      Select-Xml    7.0.0.0 Microsoft.PowerShell.Utility

Export-Clixml 可以建立一個通用的 CLI XML 檔案,讓 PowerShell 可以重建原始物件(或是非常相近的物件)

何時使用 Export-Clixml
如果取得原始物件是更理想的結果,那為何不一直使用它呢?這裡有幾個缺點:

  • 該格式往往更加龐大。
  • 該格式是專為 PowerShell 設計的,在其他語言中可能解讀起來更為複雜。
  • 在 Windows 上,PowerShell 會對檔案中與安全性相關的部分進行加密,這意味著只有建立該檔案的使用者或機器才能將它解密。

匯出

透過 Get-Help 確認 Export-Clixml 的用法

PS /Users/kanglin/code/30days> Get-Help Export-Clixml

NAME
    Export-Clixml

SYNOPSIS
    Creates an XML-based representation of an object or objects and stores it in a file.


SYNTAX
    Export-Clixml [-Depth <System.Int32>] [-Encoding {ASCII | BigEndianUnicode | BigEndianUTF32 | OEM | Unicode | UTF7 | UTF8 | UTF8BOM | UTF8NoBOM | UTF32}] [-Force] -InputObject <System.Management.Automati
    on.PSObject> -LiteralPath <System.String> [-NoClobber] [-Confirm] [-WhatIf] [<CommonParameters>]

    Export-Clixml [-Path] <System.String> [-Depth <System.Int32>] [-Encoding {ASCII | BigEndianUnicode | BigEndianUTF32 | OEM | Unicode | UTF7 | UTF8 | UTF8BOM | UTF8NoBOM | UTF32}] [-Force] -InputObject <Sy
    stem.Management.Automation.PSObject> [-NoClobber] [-Confirm] [-WhatIf] [<CommonParameters>]

匯入

透過 Get-Help 確認 Export-Clixml 的用法

PS /Users/kanglin/code/30days> Get-Help Import-Clixml

NAME
    Import-Clixml

SYNOPSIS
    Imports a CLIXML file and creates corresponding objects in PowerShell.

SYNTAX
    Import-Clixml [-First <System.UInt64>] [-IncludeTotalCount] -LiteralPath <System.String[]> [-Skip <System.UInt64>] [<CommonParameters>]

    Import-Clixml [-Path] <System.String[]> [-First <System.UInt64>] [-IncludeTotalCount] [-Skip <System.UInt64>] [<CommonParameters>]

範例

PS /Users/kanglin/code/30days> Get-Help Import-Clixml -Examples | more

NAME
    Import-Clixml

SYNOPSIS
    Imports a CLIXML file and creates corresponding objects in PowerShell.

    -- Example 1: Import a serialized file and recreate an object --

    Get-Process | Export-Clixml -Path .\pi.xml
    $Processes = Import-Clixml -Path .\pi.xml

透過 Compare-Object 比較檔案

剛剛有透過 Export-Clixm 將當下的 Process 紀錄起來(存成 XML),這裏依據書中的範例重新執行一次並將其取名為 ReferenceProcess.xml,所以等於我在 9/21 13:37 的時間點將 process 的 status 匯出成 clixml 檔案。

PS /Users/kanglin/code/30days> Get-Process | Export-Clixml ReferenceProcess
PS /Users/kanglin/code/30days> ls -lh | grep Process
-rw-r--r--@ 1 kanglin  staff   3.5M  9 21 13:57 ReferenceProcess

透過 Get-Help 確認 Compare-Object 的用法

NAME
    Compare-Object

SYNOPSIS
    Compares two sets of objects.

SYNTAX
    Compare-Object [-ReferenceObject] <System.Management.Automation.PSObject[]> [-DifferenceObject] <System.Management.Automation.PSObject[]> [-CaseSensitive] [-Culture <System.String>] [-ExcludeDifferent] [
    -IncludeEqual] [-PassThru] [-Property <System.Object[]>] [-SyncWindow <System.Int32>] [<CommonParameters>]

接著便可透過 Compare-Object 的方式去比較目前的 Process 跟剛剛的 ReferenceProcess.xml 差異在哪

Compare-Object -ReferenceObject (Import-Clixml ./ReferenceProcess) -DifferenceObject (Get-Process) -Property Name

https://ithelp.ithome.com.tw/upload/images/20240921/20168708ggvy0TEzCx.png

  1. **Compare-Object**:這個指令用來比較兩個物件(或集合)的不同之處。
  2. **-ReferenceObject**:指定參考物件,也就是用來作為比較基準的物件。在這個例子中,是透過 Import-Clixml 指令從檔案 ./ReferenceProcess 匯入的內容。這個檔案預計包含之前系統的進程清單。
  3. **-DifferenceObject**:指定需要與參考物件進行比較的物件。在這個例子中,使用 Get-Process 取得目前系統中所有正在執行的進程。
  4. **-Property Name**:指定要比較的屬性。在這裡是根據進程的 Name 屬性(即進程名稱)進行比較。

結果解釋

比較結果顯示在表格中,分為以下幾列:

  • **Name**:顯示進程的名稱。
  • **SideIndicator**:指出進程的差異來源:
    • **=>** 表示這些進程存在於 ReferenceObject(參考進程)中,但不存在於 DifferenceObject(當前進程)中,代表這些進程已經結束或不再執行。
    • **<=** 表示這些進程存在於 DifferenceObject(當前進程)中,但不存在於 ReferenceObject(參考進程)中,代表這些進程是新增的或現在正在執行。

範例解釋

根據結果:

  • Google Chrome H =>:表示 Google Chrome H 這個進程在參考進程中存在,但目前並未執行。
  • Calendar <=:表示 Calendar 這個進程目前正在執行,但在參考進程中沒有記錄到。
  • 多個 mdworker\_shared =>:表示多個 mdworker\_shared 進程在參考資料中存在但已不再執行。
  • 多個 Code Helper <=:表示多個 Code Helper 相關進程目前正在執行,但並不在原始參考中。

Output file

書中有解釋到,當 cmdlet 執行後,顯示在終端機上的表格想要保存時,我們很常會使用

Dir > DirectoryList.txt

實際上,> 是被加進去 PowerShell 的一個捷徑,目的是為了提供與 Bash Shell 的相容性。實際上,當執行該指令時,PowerShell 在底層做了以下動作:

Dir | Out-File DirectoryList.txt

另外,PowerShell 中有各種以 Out- 開頭的 cmdlet,其中一個叫做 Out-Default,當你沒有特別指定其他以 Out- 開頭的 cmdlet 時,shell 預設會使用它,即使你沒有察覺,從技術上來講

Dir

# 你就是在執行
Dir | Out-Default

# 而 Out-Default 唯一做的事是將內容轉送給 Out-Host,因此實際上是在執行
Dir | Out-Default | Out-Host

# 最終,Out-Host 將資訊顯示在螢幕上

明日主題

Day 8 - 擴充命令


上一篇
Day 6 - 管線:串接命令
下一篇
Day 8 - 擴充命令
系列文
《30天挑戰精通 PowerShell:從 Windows Server 到 Azure DevOps 自動化之旅》30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言