Петля PowerShell на скриптовых блоках
Я пытаюсь экспортировать несколько данных из коллекции объектов в файлы CSV.
Для этого у меня всегда есть следующая труба:
$users | < my filters/grouping/selects and expands> | Export-CSV ...
Вместо того, чтобы копировать / вставлять эти строки, я бы предпочел иметь хеш-таблицу с ключом в качестве имени файла 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
2 ответа
Итак, 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"]
Это обеспечивает вывод, который вы ожидаете. Все, что осталось, это поместить это в свой ForEach для $ScriptBlocks
вместе с отслеживанием вашего текущего скриптового блока:
$scriptblocks.keys|%{$sb=$_;(,$users)|& $scriptblocks["$sb"]}
Это выводит все из всех блоков скриптов. Единственная проблема, с которой вы столкнулись, заключается в том, что вы не можете указать, на какой CSV выводить данные. Но это по крайней мере отвечает на ваш оригинальный вопрос.
Проблема с этим подходом заключается в том, что он неэффективен: вам нужно пройти через все ваши $users один раз для каждого выходного файла.
Другой подход заключается в использовании switch
заявление, чтобы обойти это (psuedocode следовать):
$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}
}
Таким образом, пользователи сопоставляются с вашими критериями один за другим, и когда выполняется сопоставление, вызывается функция добавления данных этого пользователя в файл для этого типа сопоставления.
Надеюсь, что это подходящее предложение.