编写此比较 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
的用法。
我是 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
的用法。