🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
PowerShell

Windows PowerShellはコマンドラインインターフェースであり、システム管理を含むWindowsタスク自動化のためのスクリプト言語です。

Q&A

解決済

1回答

2121閲覧

PowerShell PSCustomObjectの差分確認

eri0315

総合スコア23

PowerShell

Windows PowerShellはコマンドラインインターフェースであり、システム管理を含むWindowsタスク自動化のためのスクリプト言語です。

0グッド

0クリップ

投稿2019/12/06 13:35

前提・実現したいこと

PowerShellのPSCustomObject同士で、差分確認を行いたいです。

該当のソースコード

input

1gc Config_File.json 2[{"hoge":"foo"}] 3 4gc Task_Json.json 5[{"hoge":"foo"}] 6

PowerShell

1$PSCsustomObject1 = (gc Config_File.json | ConvertFrom-Json) 2$PSCsustomObject2 = (gc Task_Json.json | ConvertFrom-Json) 3 4if($PSCsustomObject1[0] -eq $PSCsustomObject2[0]){ 5 $NOT_Processing_Flag = 1 6 } 7 8if($NOT_Processing_Flag -ne 1){ 9 Main_Process 10 } 11

試したこと

上記のソースコードを実行すると、if文の結果が、「False」になりますが、
比較演算子を「match」に変えると、結果が「True」になります。
「eq」と「match」の差は正規表現が使えるか否かの差だと、
認識しているので、今回の場合であれば、結果が同じになると想定しているのですが、
なぜ、結果が異なるのでしょうか。
また、PSCustomObjectで差分を確認する際の演算子は、どちらが正しいのでしょうか。

補足情報(FW/ツールのバージョンなど)

・PSVersion 5.1

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

また、PSCustomObjectで差分を確認する際の演算子は、どちらが正しいのでしょうか。

構造的に同じかどうかを比較したい場合はどちらも不適切です。

試したこと の動作のイメージ

まず、-eq演算子のときですが、PSCustomObjectの場合は左辺と右辺が同じ参照かどうかを比較します。

具体的には以下のような場合にTrueになります。

PowerShell

1$PSCsustomObject1[0] -eq $PSCsustomObject1[0] 2$PSCsustomObject2[0] -eq $PSCsustomObject2[0]

それ以外の場合はFalseになります。

-match演算子のときは、-match演算子は文字列を対象とした演算子のため、
左辺と右辺の両方が文字列に変換され、その上で文字列として比較が行われます。

今回の場合は、文字列化したときの結果がたまたま一致し、正規表現のメタ文字も含まれなかったため、結果がTrueとなりました。

仮に、プロパティが複数あり、文字列化したときのプロパティの表示の順番が異なっていれば、-match演算子でもFalseになります。

具体的には以下のような場合はFalseになります。

PowerShell

1$pso1, $pso2 = @(@' 2[ 3 { 4 "hoge":"foo", 5 "fuga":"bar" 6 }, 7 { 8 "fuga":"bar", 9 "hoge":"foo" 10 } 11] 12'@ | ConvertFrom-Json).ForEach({$_}) 13 14$pso1 -match $pso2

対策案

CSV化して比較

単純な値のみが含まれる場合は、ConvertTo-Csvで文字列化して比較するのが簡単だと思われます。

ConvertTo-Csvに複数のオブジェクトをパイプライン入力した場合、先頭のオブジェクトに合わせてプロパティの整列が行われるためです。

PowerShell

1$pso1, $pso2 = @(@' 2[ 3 { 4 "hoge":"foo", 5 "fuga":"bar" 6 }, 7 { 8 "fuga":"bar", 9 "hoge":"foo" 10 } 11] 12'@ | ConvertFrom-Json).ForEach({$_}) 13 14# 1行目はヘッダーなので捨てる 15$null, [string]$pso1AsCsv, [string]$pso2AsCsv = 16 @($pso1, $pso2) | 17 ConvertTo-Csv -NoTypeInformation 18 19$pso1AsCsv -eq $pso2AsCsv

各プロパティについて詳細な比較

単純に文字列化できない要素を含んでいる場合は、各プロパティを列挙して比較することになるでしょう。

いろいろ考慮不足な簡易的な実装ですが、以下のコードのようなイメージです。

PowerShell

1using namespace System.Management.Automation 2 3function Test-PSCustomObject { 4<# 5.SYNOPSIS 6二つのオブジェクトが構造的に等しいかどうかを判定します。 7簡易的な実装です。 8https://teratail.com/questions/227831 9#> 10 Param( 11 [Parameter(Mandatory = $true)] 12 [PSCustomObject]$Base 13 , 14 [Parameter(Mandatory = $true)] 15 [PSCustomObject]$Target 16 ) 17 [PSMemberInfoCollection[PSPropertyInfo]]$targeteProps = 18 $Target.psobject.Properties 19 # 各プロパティについて名前を一致するプロパティを探し、値が一致するかどうか確認する 20 [PSPropertyInfo]$baseProp = $null 21 foreach ($baseProp in $Base.psobject.Properties) { 22 [ReadOnlyPSMemberInfoCollection[PSPropertyInfo]]$matchProps = 23 $targeteProps.Match($baseProp.Name) 24 if ($matchProps.Count -eq 0) { 25 Write-Verbose -Message ('$Targetに"{0}"という名前のプロパティがありません。' -f $baseProp.Name) 26 return $false 27 } 28 29 if ($matchProps[0].Value -ne $baseProp.Value) { 30 Write-Verbose -Message ('$Targetの"{0}"という名前のプロパティの値が$Baseと異なります(Base = {1}, Target = {2})。' -f $baseProp.Name, $baseProp.Value, $matchProps[0].Value) 31 return $false 32 } 33 } 34 return $true 35}

投稿2019/12/07 12:31

imihito

総合スコア2166

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問