问题描述
好的.这就是我想要做的.我基本上是在尝试通过从 AD 和 SQL 数据库导入内容来在 powershell 中创建一个二维数组.稍后在脚本中,我将使用该数组来写入、更新和删除数据库中的行.我遇到的问题是将所有内容都放入一个变量中.
当我运行我的代码时,它一遍又一遍地将同一个用户添加到所需的数组中.创建到 $Table 变量中的最后一项被写入 $ToWrite 中的每个实例.
我尝试在将所有变量输入 $Table 后暂停,将 $Table 写入控制台,然后将 $ToWrite 写入控制台.$Table 在每个循环中都会正确更改,但同样,一旦它写入 $ToWrite,它就会覆盖每个实例.我尝试了多种方法,例如 .add()、PsCustomObject[] 等.我现在卡住了.
这是我的半功能代码:
$AllPOCs = Get-ADGroupMember 'ALL POC'$POCs = @()$计数器 = 0$AllPOCs |ForEach-Object {$Name = $AllPOCs[$Counter].SamAccountName$TestPOC = Invoke-Sqlcmd -Query "SELECT * FROM TABLE WHERE CLIENT = '$Name'" -ServerInstance "SERVER\INSTANCE"如果($TestPOC -eq $null){$POCs += Get-ADUser $Name -Properties * |选择 - 属性 SamAccountName、GivenName、Surname、SID、EmailAddress}$计数器 += 1}$ToWrite = @()$计数器 = 0$SEQUENCE = Invoke-Sqlcmd -Query "Select TABLEFIELD FROM DATABASE WHERE NAME = 'FIELDID'" -ServerInstance "SERVER\INSTANCE"$SEQUENCE = $SEQUENCE.RECNUM + 1$Table = "" |选择序列、LASTUSER、组、客户、FNAME、姓名、电子邮件、USEDEPT、USELOCATION、CREATEDFROMSSD、DISPLAYCLIENTCOMMENTS、_INACTIVE_、WINUSERID、SELFSERVICEACCESS、SELFSERVICELICENSE、WIAENABLED、SID#到这里一切正常$POC |ForEach-Object {$Table.SEQUENCE = $SEQUENCE$Table.LASTUSER = 'SYSTEMACCOUNT'$Table.GROUP = 1$Table.CLIENT = $POCs[$Counter].SamAccountName.ToUpper()$Table.FNAME = $POCs[$Counter].GivenName.ToString()$Table.Name = $POCs[$Counter].surname.ToString()$Table.EmailID = 'SMTP:{' + $POCs[$Counter].EmailAddress.ToString() + '}' +$POCs[$Counter].EmailAddress.ToString()$Table.USEDEPT = 0$Table.USELOCATION = 0$Table.CREATEDFROMSSD = 0$Table.DISPLAYCLIENTCOMMENTS = 0$Table._INACTIVE_ = 0$Table.WINUSERID = '\DOMAIN' + $POCs[$Counter].SamAccountName.ToString()$Table.SELFSERVICEACCESS = '类型'$Table.SELFSERVICELICENSE = 1$Table.WIAENABLED = 1$Table.SID = $POCs[$Counter].SID.ToString()$ToWrite += $Table #这不能正常工作.$序列 += 1$计数器 += 1}
输出示例:
序列 : 1206上一个用户:系统帐户组 : 1客户:用户名FNAME : 名字姓名:姓氏电子邮件:SMTP:{EMAIL}EMAIL使用深度:0用途 : 0从SSD创建:0显示客户评论:0_未激活_:0WINUSERID : 域\用户名自助服务:类型自助服务许可证:1无障碍 : 1SID : S-1-Z-XX-CCCCCCCCCC-YYYYYYYYY-VVVVVVVVVV-125121序列 : 1206上一个用户:系统帐户组 : 1客户:用户名FNAME : 名字姓名:姓氏电子邮件:SMTP:{EMAIL}EMAIL使用深度:0用途 : 0从SSD创建:0显示客户评论:0_未激活_:0WINUSERID : 域\用户名自助服务:类型自助服务许可证:1无障碍 : 1SID : S-1-Z-XX-CCCCCCCCCC-YYYYYYYYY-VVVVVVVVVV-125121序列 : 1206上一个用户:系统帐户组 : 1客户:用户名FNAME : 名字姓名:姓氏电子邮件:SMTP:{EMAIL}EMAIL使用深度:0用途 : 0从SSD创建:0显示客户评论:0_未激活_:0WINUSERID : 域\用户名自助服务:类型自助服务许可证:1无障碍 : 1SID : S-1-Z-XX-CCCCCCCCCC-YYYYYYYYY-VVVVVVVVVV-125121有什么想法吗?
问题是您要一遍又一遍地更新相同的对象 $Table
并添加引用到输出数组的同一个对象,$ToWrite
- 因此其所有元素最终都指向一个且唯一的 $Table
对象,其属性值那时包含在上次迭代中分配的那些.
在这个答案中详细解释了该问题,其中显示了使用自定义类的可能解决方案em>,在 PowerShell v5 及更高版本中可用.
没有自定义类的解决方案要求您在每次迭代中克隆自定义$Table
对象:
# 创建一个具有相同属性的新实例:$Table = $Table.psobject.Copy()
注意:这种克隆技术仅适用于自定义对象,即[System.Management.Automation.PSCustomObject]
的实例,例如由 Select-Object
cmdlet 和文字语法 [pscustomobject] @{ ... }
也就是说,由于您要在循环中为自定义对象的所有属性赋值,因此预先创建模板对象没有任何好处 - 相反,简单地使用文字自定义对象创建语法[pscustomobject] @{ ... }
(PSv3+) 在你的循环中,它隐式地在每次迭代中创建一个新实例.
此外,您的解决方案可以简化,因为让 PowerShell 为您创建数组既简单又高效,只需收集输出变量中多个对象的命令的输出.
这里有一个简单的例子,将所有内容放在一起:
# 循环输入并实例化一个新的自定义对象# 在每次迭代中,然后让 PowerShell 收集结果# 在数组变量 $ToWrite 中[数组] $ToWrite = 1..3 |ForEach-Object {# 在每次迭代中实例化并输出一个新的自定义对象.[pscustomobject] @{PropA = "ValueA-$_"PropB = "ValueB-$_"}}# 输出结果数组$ToWrite
以上产生以下结果,表明创建了不同的对象:
PropA PropB----- -----值A-1 值B-1值A-2 值B-2值A-3 值B-3
Okay. Here's what I'm trying to do. I'm basically trying to make a 2D array in powershell by importing stuff from AD and a SQL database. I'm going to use the array to write, update, and delete rows in the database later in the script. What I'm having a problem with, is getting everything into one variable.
When I run my code, it adds the same user over and over again into the desired array. The very last item in the created to the $Table variable gets written over every single instance inside the $ToWrite.
I tried pausing after inputting all the variables into $Table, writing $Table to console, then writing $ToWrite to console. The $Table changes correctly in every loop, but again, once it writes to $ToWrite it overwrites each instance. I've tried multiple ways like .add(), PsCustomObject[], etc. I'm just stuck now.
Here's my semi-functioning code:
$AllPOCs = Get-ADGroupMember 'ALL POC'
$POCs = @()
$Counter = 0
$AllPOCs | ForEach-Object {
$Name = $AllPOCs[$Counter].SamAccountName
$TestPOC = Invoke-Sqlcmd -Query "SELECT * FROM TABLE WHERE CLIENT = '$Name'" -ServerInstance "SERVER\INSTANCE"
if ($TestPOC -eq $null) {
$POCs += Get-ADUser $Name -Properties * |
select -Property SamAccountName, GivenName, Surname, SID, EmailAddress
}
$Counter += 1
}
$ToWrite = @()
$Counter = 0
$SEQUENCE = Invoke-Sqlcmd -Query "Select TABLEFIELD FROM DATABASE WHERE NAME = 'FIELDID'" -ServerInstance "SERVER\INSTANCE"
$SEQUENCE = $SEQUENCE.RECNUM + 1
$Table = "" | select SEQUENCE, LASTUSER, GROUP, CLIENT, FNAME, NAME, EMAILID, USEDEPT, USELOCATION, CREATEDFROMSSD, DISPLAYCLIENTCOMMENTS, _INACTIVE_, WINUSERID, SELFSERVICEACCESS, SELFSERVICELICENSE, WIAENABLED, SID
#everything works correctly up to here
$POCs | ForEach-Object {
$Table.SEQUENCE = $SEQUENCE
$Table.LASTUSER = 'SYSTEMACCOUNT'
$Table.GROUP = 1
$Table.CLIENT = $POCs[$Counter].SamAccountName.ToUpper()
$Table.FNAME = $POCs[$Counter].GivenName.ToString()
$Table.Name = $POCs[$Counter].surname.ToString()
$Table.EmailID = 'SMTP:{' + $POCs[$Counter].EmailAddress.ToString() + '}' +
$POCs[$Counter].EmailAddress.ToString()
$Table.USEDEPT = 0
$Table.USELOCATION = 0
$Table.CREATEDFROMSSD = 0
$Table.DISPLAYCLIENTCOMMENTS = 0
$Table._INACTIVE_ = 0
$Table.WINUSERID = '\DOMAIN' + $POCs[$Counter].SamAccountName.ToString()
$Table.SELFSERVICEACCESS = 'TYPE'
$Table.SELFSERVICELICENSE = 1
$Table.WIAENABLED = 1
$Table.SID = $POCs[$Counter].SID.ToString()
$ToWrite += $Table #THIS DOESN'T WORK PROPERLY.
$SEQUENCE += 1
$Counter += 1
}
Output example:
SEQUENCE : 1206 LASTUSER : SYSTEMACCOUNT GROUP : 1 CLIENT : USERNAME FNAME : FIRSTNAME NAME : LASTNAME EMAILID : SMTP:{EMAIL}EMAIL USEDEPT : 0 USELOCATION : 0 CREATEDFROMSSD : 0 DISPLAYCLIENTCOMMENTS : 0 _INACTIVE_ : 0 WINUSERID : DOMAIN\USERNAME SELFSERVICEACCESS : TYPE SELFSERVICELICENSE : 1 WIAENABLED : 1 SID : S-1-Z-XX-CCCCCCCCCC-YYYYYYYYY-VVVVVVVVVV-125121 SEQUENCE : 1206 LASTUSER : SYSTEMACCOUNT GROUP : 1 CLIENT : USERNAME FNAME : FIRSTNAME NAME : LASTNAME EMAILID : SMTP:{EMAIL}EMAIL USEDEPT : 0 USELOCATION : 0 CREATEDFROMSSD : 0 DISPLAYCLIENTCOMMENTS : 0 _INACTIVE_ : 0 WINUSERID : DOMAIN\USERNAME SELFSERVICEACCESS : TYPE SELFSERVICELICENSE : 1 WIAENABLED : 1 SID : S-1-Z-XX-CCCCCCCCCC-YYYYYYYYY-VVVVVVVVVV-125121 SEQUENCE : 1206 LASTUSER : SYSTEMACCOUNT GROUP : 1 CLIENT : USERNAME FNAME : FIRSTNAME NAME : LASTNAME EMAILID : SMTP:{EMAIL}EMAIL USEDEPT : 0 USELOCATION : 0 CREATEDFROMSSD : 0 DISPLAYCLIENTCOMMENTS : 0 _INACTIVE_ : 0 WINUSERID : DOMAIN\USERNAME SELFSERVICEACCESS : TYPE SELFSERVICELICENSE : 1 WIAENABLED : 1 SID : S-1-Z-XX-CCCCCCCCCC-YYYYYYYYY-VVVVVVVVVV-125121
Any ideas?
The problem is that you're updating the very same object, $Table
, over and over, and adding references to that same object to the output array, $ToWrite
- all of whose elements therefore end up pointing to the one and only $Table
object, whose property values at that point contain the ones that were assigned in the last iteration.
The problem is explained in detail in this answer, which shows a possible solution using custom classes, available in PowerShell v5 and above.
A solution without custom classes requires you to clone your custom $Table
object in each iteration:
# Create a new instance with the same properties:
$Table = $Table.psobject.Copy()
Note: This cloning technique only works as expected with custom objects, i.e., instances of [System.Management.Automation.PSCustomObject]
, such as created by the Select-Object
cmdlet and literal syntax [pscustomobject] @{ ... }
That said, since you're assigning to all properties of your custom object in your loop, there is no benefit to creating a template object up front - instead, simply use literal custom-object creation syntax [pscustomobject] @{ ... }
(PSv3+) inside your loop, which implicitly creates a new instance in every iteration.
Additionally, your solution can be streamlined, because it is both simpler and more efficient to let PowerShell create arrays for you, simply by collecting the output from commands that output multiple objects in a variable.
Here's a simplified example that puts it all together:
# Loop over the input and instantiate a new custom object
# in each iteration, then let PowerShell collect the results
# in array variable $ToWrite
[array] $ToWrite = 1..3 | ForEach-Object {
# Instantiate and output a new custom object in each iteration.
[pscustomobject] @{
PropA = "ValueA-$_"
PropB = "ValueB-$_"
}
}
# Output the resulting array
$ToWrite
The above yields the following, showing that distinct objects were created:
PropA PropB
----- -----
ValueA-1 ValueB-1
ValueA-2 ValueB-2
ValueA-3 ValueB-3
这篇关于动态地将项目添加到集合数组中的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!