Вам следует объединить два внешних цикла foreach
не только потому, что они повторяют одну и ту же коллекцию, но и потому, что второй l oop пытается использовать переменную $data
, созданную в первом l oop. Кроме того, поскольку Export-Csv
вызывается в al oop, вам нужно будет передать параметр -Append
, чтобы предотвратить перезапись выходного файла каждый раз.
$Users = (Get-Content C:\temp\Users.csv) -notmatch '^\s*$'
foreach ($User in $Users) {
$Shortcuts = Get-ChildItem -Recurse \\UNCPATHTOUSERPROFILES\$User -Include *.lnk
$Shell = New-Object -ComObject WScript.Shell
$data = foreach ($Shortcut in $Shortcuts) {
[PSCustomObject]@{
ShortcutName = $Shortcut.Name;
Target = $Shell.CreateShortcut($Shortcut).targetpath
User = $User
}
}
$data | Export-Csv c:\temp\report.csv -Append -NoTypeInformation
[Runtime.InteropServices.Marshal]::ReleaseComObject($Shell) | Out-Null
}
Вы также можете исключить $Shortcuts
и $data
переменные в пользу использования конвейера ...
$Users = (Get-Content C:\temp\Users.csv) -notmatch '^\s*$'
foreach ($User in $Users) {
$Shell = New-Object -ComObject WScript.Shell
Get-ChildItem -Recurse \\UNCPATHTOUSERPROFILES\$User -Include *.lnk `
| ForEach-Object -Process {
[PSCustomObject]@{
ShortcutName = $_.Name;
Target = $Shell.CreateShortcut($_).targetpath
User = $User
}
} `
| Export-Csv c:\temp\report.csv -Append -NoTypeInformation
[Runtime.InteropServices.Marshal]::ReleaseComObject($Shell) | Out-Null
}
... но обратите внимание, что -Append
все еще требуется. Наконец, вы можете переписать все это, используя конвейер ...
$Shell = New-Object -ComObject WScript.Shell
try
{
Get-Content C:\temp\Users.csv `
| Where-Object { $_ -notmatch '^\s*$' } -PipelineVariable 'User' `
| ForEach-Object -Process { "\\UNCPATHTOUSERPROFILES\$User" } `
| Get-ChildItem -Recurse -Include *.lnk `
| ForEach-Object -Process {
[PSCustomObject]@{
ShortcutName = $_.Name;
Target = $Shell.CreateShortcut($_).targetpath
User = $User
} `
} `
| Export-Csv c:\temp\report.csv -NoTypeInformation
}
finally
{
[Runtime.InteropServices.Marshal]::ReleaseComObject($Shell) | Out-Null
}
... так что report.csv
открывается только для записи один раз, без необходимости -Append
. Обратите внимание, что я создаю один экземпляр WScript.Shell
и использую try
/ finally
, чтобы обеспечить его освобождение.
В случае, если у пользователя в Users.csv
нет каталога в \\UNCPATHTOUSERPROFILES
, любой из приведенных выше решений и кода в вопросе будет выдавать ошибку, когда Get-ChildItem
пытается перечислить этот каталог. Это можно исправить, проверив, что каталог существует первым или передав -ErrorAction SilentlyContinue
в Get-ChildItem
, или вы можете перечислить \\UNCPATHTOUSERPROFILES
и отфильтровать те, которые встречаются в Users.csv
...
$Shell = New-Object -ComObject WScript.Shell
try
{
# Performs faster filtering and eliminates duplicate user rows
$UsersTable = Get-Content C:\temp\Users.csv `
| Where-Object { $_ -notmatch '^\s*$' } `
| Group-Object -AsHashTable
# Get immediate child directories of \\UNCPATHTOUSERPROFILES
Get-ChildItem -Path \\UNCPATHTOUSERPROFILES -Directory -PipelineVariable 'UserDirectory' `
<# Filter for user directories specified in Users.csv #> `
| Where-Object { $UsersTable.ContainsKey($UserDirectory.Name) } `
<# Get *.lnk descendant files of the user directory #> `
| Get-ChildItem -Recurse -Include *.lnk -File `
| ForEach-Object -Process {
[PSCustomObject]@{
ShortcutName = $_.Name;
Target = $Shell.CreateShortcut($_).targetpath
User = $UserDirectory.Name
} `
} `
| Export-Csv c:\temp\report.csv -NoTypeInformation
}
finally
{
[Runtime.InteropServices.Marshal]::ReleaseComObject($Shell) | Out-Null
}