编写此比较 PowerShell 脚本的更好方法是什么?

What is a better way to write this comparative PowerShell script?

我是 PowerShell 的新手,我确定我在这里没有使用最佳实践。我一直在研究这个 PowerShell 脚本来比较两个 XML 文件。我首先遍历每个 XML 文件并将数据放入 PS 对象中:

以下是 XML 数据的一些示例:

XML 文件 1

<RESULTS>
    <ROW>
        <COLUMN NAME="ATTR1"><![CDATA[123456ABCDEF]]></COLUMN>
        <COLUMN NAME="ATTR2"><![CDATA[1.0.4.0]]></COLUMN>
        <COLUMN NAME="ATTR3"><![CDATA[Google.com]]></COLUMN>
        <COLUMN NAME="ATTR4"><![CDATA[Lorem ipsum]]></COLUMN>
        <COLUMN NAME="ATTR5"><![CDATA[This is some text]]></COLUMN>
    </ROW>
    <ROW>
        <COLUMN NAME="ATTR1"><![CDATA[123456ABCDEF]]></COLUMN>
        <COLUMN NAME="ATTR2"><![CDATA[2.0.0.1]]></COLUMN>
        <COLUMN NAME="ATTR3"><![CDATA[HelloWorld.com]]></COLUMN>
        <COLUMN NAME="ATTR4"><![CDATA[Lorem ipsum]]></COLUMN>
        <COLUMN NAME="ATTR5"><![CDATA[This is some text]]></COLUMN>
    </ROW>
    <ROW>
        <COLUMN NAME="ATTR1"><![CDATA[123456ABCDEF]]></COLUMN>
        <COLUMN NAME="ATTR2"><![CDATA[5.6.7.0]]></COLUMN>
        <COLUMN NAME="ATTR3"><![CDATA[foo_foo_6 (2).org]]></COLUMN>
        <COLUMN NAME="ATTR4"><![CDATA[Lorem ipsum]]></COLUMN>
        <COLUMN NAME="ATTR5"><![CDATA[This is some text]]></COLUMN>
    </ROW>
</RESULTS>

XML 文件 2

<applications xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <application>
        <name>Google.com</name>
        <version>1.2.0.0</version>
    </application>
    <application>
        <name>HelloWorld.com</name>
        <version>2.0.0.1</version>
    </application>
    <application>
        <name>FOO_FOO.org</name>
        <version>6.2.0.1</version>
    </application>
</applications>

创建 arrays/objects 填充 XML 数据

# assign all output from `foreach` loop to `$array1` - XML file 1
$array1 = foreach($row in $xmldata1.RESULTS.ROW){
  # create new object with the pertinent details as property values
  [pscustomobject]@{
    Name = $row.COLUMN.Where{ $_.NAME -eq "ATTR3"}.'#cdata-section'
    Version = $row.COLUMN.Where{ $_.NAME -eq "ATTR2"}.'#cdata-section'
  }
}

# assign all output from `foreach` loop to `$array2` - XML file 2
$array2 = foreach($row in $xmldata2.applications.application){
  # create new object with the pertinent details as property values
  [pscustomobject]@{
    Name = $row.name
    Version = $row.version
  }
}

这是我想知道如何更有效地编写的脚本。它只是循环遍历 $array1 并将其与 $array2 中的数据进行比较。如果名称匹配但版本不匹配,那么它会将这些值存储在 PS 对象中。

脚本我要改进

#loop through array 1
for($i = 0; $i -le $array1.Length; $i++)
{
    #loop through array 2
    for($j = 0; $j -le $array2.Length; $j++)
    {
        #if file name in array 1 matches a name in array 2...
        if (($array1.name[$i] -eq $array2.name[$j]) -or ($array1.name[$i].Substring(0, [Math]::Min($array1.name[$i].Length, 7)) -eq $array2.name[$j].Substring(0, [Math]::Min($array2.name[$i].Length, 7))))
        {
            #then, if that file names version does not match the version found in array 2...
            if($array1.version[$i] -ne $array2.version[$j])
            {
                #create new object        
                [pscustomobject]@{
                    Name = $array1.name[$i]
                    Name2 = $array2.name[$j]
                    Version = $array1.version[$i]
                    Version2 = $array2.version[$j]            
                }
            }
        }
    }
}

但是,有些名称并不完全匹配。所以我使用 -or 运算符并将这一行放在我的第一个 if-statement 中来比较每个数组中文件名的前 7 个字符,看看是否有某种匹配(我知道有):

($array1.name[$i].Substring(0, [Math]::Min($array1.name[$i].Length, 7)) -eq $array2.name[$j].Substring(0, [Math]::Min($array2.name[$i].Length, 7)))

每当我添加该行时,尽管我只收到数组中 某些 数据对象的以下错误。该脚本将 return 一些对象,但大多数时候我的控制台窗格将充满以下错误:

错误

You cannot call a method on a null-valued expression.
At line:8 char:13
+         if (($array1.name[$i] -eq $array2.name[$j]) -or ($array1 ...
+             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull

我什至不知道它在说什么。因为当我提取该行并将实际索引放入其中时,它工作正常。

例子

if($array1.name[1020].Substring(0, [Math]::Min($array1.name[1020].Length, 7)) -eq $array2.name[2500].Substring(0, [Math]::Min($array2.name[2500].Length, 7))){

所以,我很难过。有没有更好的方法来比较这两个数组并获得相似的输出?

我相信这可行,而且可能是一种更直接的方法,这种方法不需要您进行第一个 XML 的对象构造。希望内联评论解释了逻辑。

:outer foreach($i in $xml1.results.row) {
    $name = $i.Column.Where{ $_.NAME -eq 'ATTR3' }.'#cdata-section'
    $version = $i.Column.Where{ $_.NAME -eq 'ATTR2' }.'#cdata-section'
    foreach($z in $xml2.applications.application) {
        # check if they have the same version
        $sameVersion = $version -eq $z.Version
        # check if they have the same name
        $sameName = $name -eq $z.Name
        # if both conditions are `$true` we can skip this and continue with
        # next item of outer loop
        if($sameVersion -and $sameName) {
            continue outer
        }
        # if their first 7 characters are the same but they're NOT the same version
        if([string]::new($name[0..6]) -eq [string]::new($z.Name[0..6]) -and -not $sameVersion) {
            [pscustomobject]@{
                Name     = $name
                Name2    = $z.Name
                Version  = $version
                Version2 = $z.Version
            }
        }
    }
}

结果将是:

Name              Name2       Version Version2
----              -----       ------- --------
Google.com        Google.com  1.0.4.0 1.2.0.0
foo_foo_6 (2).org FOO_FOO.org 5.6.7.0 6.2.0.1

请参阅 Using a labeled continue in a loop,其中描述并解释了此示例中 continue outer 的用法。