脚本块上的 powershell 循环
powershell Loop on scriptblocks
我正在尝试将对象集合中的一些信息导出到 CSV 文件中。
为此,我总是使用以下管道:
$users | < my filters/grouping/selects and expands> | Export-CSV ...
而不是 copy/paste 这些行,我更希望有一个哈希表,其中 CSV 文件名作为键,< ... >
之间的部分作为值
所以我这样做了:
$scriptblocks = @{"NonCompliantMail"={ ? {-not ([bool]($_.mail -as [Net.Mail.MailAddress])) } };
"NonCompliantSAM"= { ? { ($_.samaccountname.Trim().Length - $_.samaccountname.Length) -ne 0 }};
"MissingSN" = { ? {[string]::IsNullOrWhiteSpace($_.sn) } };
"MissingGivenName" = { ? {[string]::IsNullOrWhiteSpace($_.givenname) } };
"TrimSN" = { ? { (-not ([string]::IsNullOrWhiteSpace($_.sn))) -and (($_.sn.Trim().Length - $_.sn.Length) -ne 0) } };
"TrimGivenName" = { ? { (-not ([string]::IsNullOrWhiteSpace($_.givenname))) -and (($_.givenname.Trim().Length - $_.givenname.Length) -ne 0) } }
"MultipleEmails" = { group-object mail |? { $_.Count -gt 1 } | select -ExpandProperty Group | select mail,samaccountname }
}
我正在尝试那样执行它,但它不起作用:
$scriptblocks.getEnumerator() |% { $users | & $_.Value | Export-Csv -NoTypeInformation -Force -Encoding $encoding -Delimiter $delimiter -Path (Join-Path $scriptpath ($_.Key + ".csv")) }
有什么想法吗?
问候。
JB
好的,所以 Switch 是一个循环,它根据一组过滤器测试数组的每个记录。如果记录通过过滤器,它 运行 针对以下脚本块。过滤器可以是文字匹配、RegEx 匹配或类似于 Where 语句的脚本块。根据您的需要,我们将使用其中的最后一个。查看此示例,看看它是否实现了您的目标:
Switch($users){
{-not ([bool]($_.mail -as [Net.Mail.MailAddress])) }{$_|Export-Csv -NoTypeInformation -Force -Encoding $encoding -Delimiter $delimiter -Path (Join-Path $scriptpath ("NonCompliantMail" + ".csv")) -append}
{($_.samaccountname.Trim().Length - $_.samaccountname.Length) -ne 0 }{$_|Export-Csv -NoTypeInformation -Force -Encoding $encoding -Delimiter $delimiter -Path (Join-Path $scriptpath ("NonCompliantSAM" + ".csv")) -append}
{[string]::IsNullOrWhiteSpace($_.sn) }{$_|Export-Csv -NoTypeInformation -Force -Encoding $encoding -Delimiter $delimiter -Path (Join-Path $scriptpath ("MissingSN" + ".csv")) -append}
{[string]::IsNullOrWhiteSpace($_.givenname) }{$_|Export-Csv -NoTypeInformation -Force -Encoding $encoding -Delimiter $delimiter -Path (Join-Path $scriptpath ("MissingGivenName" + ".csv")) -append}
{(-not ([string]::IsNullOrWhiteSpace($_.sn))) -and (($_.sn.Trim().Length - $_.sn.Length) -ne 0) }{$_|Export-Csv -NoTypeInformation -Force -Encoding $encoding -Delimiter $delimiter -Path (Join-Path $scriptpath ("TrimSN" + ".csv")) -append}
{(-not ([string]::IsNullOrWhiteSpace($_.givenname))) -and (($_.givenname.Trim().Length - $_.givenname.Length) -ne 0) }{$_|Export-Csv -NoTypeInformation -Force -Encoding $encoding -Delimiter $delimiter -Path (Join-Path $scriptpath ("TrimGivenName" + ".csv")) -append}
}
这将 运行 每个用户通过开关,如果它符合任何条件,它将附加到关联的 CSV 文件。
编辑: 好吧,你不喜欢 Switch。如果您真的希望能够像这样在 ForEach-Object 循环中执行脚本块,您可以向脚本块添加参数以允许管道数据,但这并不能完全解决您的问题。稍后我会谈到这一点。首先,让我们使用您的 Group-Object mail
选项并将其设置为接受输入:
"MultipleEmails" = { group-object mail |? { $_.Count -eq 1 } | select -ExpandProperty Group | select mail,samaccountname }
变成
"MultipleEmails" = {Param([Parameter(ValueFromPipeline=$True)][Object[]]$Users);$Users| group-object mail |? { $_.Count -eq 1 } | select -ExpandProperty Group | select mail,samaccountname }
我在脚本块的开头添加了 Param([Parameter(ValueFromPipeline=$True)][Object[]]$Users);$Users|
来执行此操作。您可以将其添加到每个脚本块的开头,它们都应该 运行 类似。
然后我们必须强制将 $users
作为数组传递给它,方法是这样包装它:(,$users)
这允许:
(,$users)|& $scriptblocks["multipleemails"]
这提供了您期望的输出。剩下的就是将它放在 $ScriptBlocks
的 ForEach 中,同时跟踪您当前的脚本块:
$scriptblocks.keys|%{$sb=$_;(,$users)|& $scriptblocks["$sb"]}
输出所有脚本块的所有内容。您现在遇到的唯一问题是您无法指定要输出到的 CSV。但这至少回答了你原来的问题。
这种方法的问题在于它效率低下:您必须为每个输出文件遍历所有 $users 一次。
另一种方法是使用 switch
语句来解决这个问题(遵循伪代码):
$file1 = "NonCompliantMail.csv"
$file2 = "NonCompliantSAM.csv"
$file3 = "MissingSN.csv"
$file4 = "MissingGivenName.csv"
$file5 = "TrimSN.csv"
$file6 = "TrimGivenName.csv"
$file7 = "UsersWhoDontMatchAnything.csv"
function WriteUserDataToFile
{
param
(
$User,
$OutFile
)
"Process the $User object to append to $OutFile"
}
switch ($users)
{
("User matching criteria 1") {WriteUserDataToFile $_ $file1}
("User matching criteria 2") {WriteUserDataToFile $_ $file2}
("User matching criteria 3") {WriteUserDataToFile $_ $file3}
("User matching criteria 4") {WriteUserDataToFile $_ $file4}
("User matching criteria 5") {WriteUserDataToFile $_ $file5}
("User matching criteria 6") {WriteUserDataToFile $_ $file6}
default {WriteUserDataToFile $_ $file7}
}
因此,用户会根据您的条件逐一匹配,并且在进行匹配时,会调用将该用户的数据附加到该类型匹配的文件中的函数。
希望这是一个合适的建议。
我正在尝试将对象集合中的一些信息导出到 CSV 文件中。
为此,我总是使用以下管道:
$users | < my filters/grouping/selects and expands> | Export-CSV ...
而不是 copy/paste 这些行,我更希望有一个哈希表,其中 CSV 文件名作为键,< ... >
之间的部分作为值所以我这样做了:
$scriptblocks = @{"NonCompliantMail"={ ? {-not ([bool]($_.mail -as [Net.Mail.MailAddress])) } };
"NonCompliantSAM"= { ? { ($_.samaccountname.Trim().Length - $_.samaccountname.Length) -ne 0 }};
"MissingSN" = { ? {[string]::IsNullOrWhiteSpace($_.sn) } };
"MissingGivenName" = { ? {[string]::IsNullOrWhiteSpace($_.givenname) } };
"TrimSN" = { ? { (-not ([string]::IsNullOrWhiteSpace($_.sn))) -and (($_.sn.Trim().Length - $_.sn.Length) -ne 0) } };
"TrimGivenName" = { ? { (-not ([string]::IsNullOrWhiteSpace($_.givenname))) -and (($_.givenname.Trim().Length - $_.givenname.Length) -ne 0) } }
"MultipleEmails" = { group-object mail |? { $_.Count -gt 1 } | select -ExpandProperty Group | select mail,samaccountname }
}
我正在尝试那样执行它,但它不起作用:
$scriptblocks.getEnumerator() |% { $users | & $_.Value | Export-Csv -NoTypeInformation -Force -Encoding $encoding -Delimiter $delimiter -Path (Join-Path $scriptpath ($_.Key + ".csv")) }
有什么想法吗?
问候。 JB
好的,所以 Switch 是一个循环,它根据一组过滤器测试数组的每个记录。如果记录通过过滤器,它 运行 针对以下脚本块。过滤器可以是文字匹配、RegEx 匹配或类似于 Where 语句的脚本块。根据您的需要,我们将使用其中的最后一个。查看此示例,看看它是否实现了您的目标:
Switch($users){
{-not ([bool]($_.mail -as [Net.Mail.MailAddress])) }{$_|Export-Csv -NoTypeInformation -Force -Encoding $encoding -Delimiter $delimiter -Path (Join-Path $scriptpath ("NonCompliantMail" + ".csv")) -append}
{($_.samaccountname.Trim().Length - $_.samaccountname.Length) -ne 0 }{$_|Export-Csv -NoTypeInformation -Force -Encoding $encoding -Delimiter $delimiter -Path (Join-Path $scriptpath ("NonCompliantSAM" + ".csv")) -append}
{[string]::IsNullOrWhiteSpace($_.sn) }{$_|Export-Csv -NoTypeInformation -Force -Encoding $encoding -Delimiter $delimiter -Path (Join-Path $scriptpath ("MissingSN" + ".csv")) -append}
{[string]::IsNullOrWhiteSpace($_.givenname) }{$_|Export-Csv -NoTypeInformation -Force -Encoding $encoding -Delimiter $delimiter -Path (Join-Path $scriptpath ("MissingGivenName" + ".csv")) -append}
{(-not ([string]::IsNullOrWhiteSpace($_.sn))) -and (($_.sn.Trim().Length - $_.sn.Length) -ne 0) }{$_|Export-Csv -NoTypeInformation -Force -Encoding $encoding -Delimiter $delimiter -Path (Join-Path $scriptpath ("TrimSN" + ".csv")) -append}
{(-not ([string]::IsNullOrWhiteSpace($_.givenname))) -and (($_.givenname.Trim().Length - $_.givenname.Length) -ne 0) }{$_|Export-Csv -NoTypeInformation -Force -Encoding $encoding -Delimiter $delimiter -Path (Join-Path $scriptpath ("TrimGivenName" + ".csv")) -append}
}
这将 运行 每个用户通过开关,如果它符合任何条件,它将附加到关联的 CSV 文件。
编辑: 好吧,你不喜欢 Switch。如果您真的希望能够像这样在 ForEach-Object 循环中执行脚本块,您可以向脚本块添加参数以允许管道数据,但这并不能完全解决您的问题。稍后我会谈到这一点。首先,让我们使用您的 Group-Object mail
选项并将其设置为接受输入:
"MultipleEmails" = { group-object mail |? { $_.Count -eq 1 } | select -ExpandProperty Group | select mail,samaccountname }
变成
"MultipleEmails" = {Param([Parameter(ValueFromPipeline=$True)][Object[]]$Users);$Users| group-object mail |? { $_.Count -eq 1 } | select -ExpandProperty Group | select mail,samaccountname }
我在脚本块的开头添加了 Param([Parameter(ValueFromPipeline=$True)][Object[]]$Users);$Users|
来执行此操作。您可以将其添加到每个脚本块的开头,它们都应该 运行 类似。
然后我们必须强制将 $users
作为数组传递给它,方法是这样包装它:(,$users)
这允许:
(,$users)|& $scriptblocks["multipleemails"]
这提供了您期望的输出。剩下的就是将它放在 $ScriptBlocks
的 ForEach 中,同时跟踪您当前的脚本块:
$scriptblocks.keys|%{$sb=$_;(,$users)|& $scriptblocks["$sb"]}
输出所有脚本块的所有内容。您现在遇到的唯一问题是您无法指定要输出到的 CSV。但这至少回答了你原来的问题。
这种方法的问题在于它效率低下:您必须为每个输出文件遍历所有 $users 一次。
另一种方法是使用 switch
语句来解决这个问题(遵循伪代码):
$file1 = "NonCompliantMail.csv"
$file2 = "NonCompliantSAM.csv"
$file3 = "MissingSN.csv"
$file4 = "MissingGivenName.csv"
$file5 = "TrimSN.csv"
$file6 = "TrimGivenName.csv"
$file7 = "UsersWhoDontMatchAnything.csv"
function WriteUserDataToFile
{
param
(
$User,
$OutFile
)
"Process the $User object to append to $OutFile"
}
switch ($users)
{
("User matching criteria 1") {WriteUserDataToFile $_ $file1}
("User matching criteria 2") {WriteUserDataToFile $_ $file2}
("User matching criteria 3") {WriteUserDataToFile $_ $file3}
("User matching criteria 4") {WriteUserDataToFile $_ $file4}
("User matching criteria 5") {WriteUserDataToFile $_ $file5}
("User matching criteria 6") {WriteUserDataToFile $_ $file6}
default {WriteUserDataToFile $_ $file7}
}
因此,用户会根据您的条件逐一匹配,并且在进行匹配时,会调用将该用户的数据附加到该类型匹配的文件中的函数。
希望这是一个合适的建议。