【问题标题】:Export-Csv Producing Empty Output FileExport-Csv 生成空输出文件
【发布时间】:2021-02-26 18:48:28
【问题描述】:

我有一个脚本来审核本地用户帐户。一切正常,除了导出到 CSV。输出文件为空白,列标题除外:

Computer Name Enabled PasswordChangeableDate PasswordExpires UserMayChangePassword PasswordRequired PasswordLastSet LastLogon

我在这里做错了什么?导出部分朝向底部。

$Result = New-Object System.Collections.Generic.List[System.Object] #store local user data
$Errors = New-Object System.Collections.Generic.List[System.Object] #store any devices that could not be reached

$devices = Get-Content "list.txt" #read in all devices to check

#for progress bar - store current # and total
$length = $devices.Count
$i = 1

$displayOutputReport = $true

foreach ($device in $devices){
    Write-Progress -Activity "Retrieving data from $device ($i of $length)" -percentComplete  ($i++*100/$length) -status Running

    if (Test-Connection -ComputerName $device -Count 1 -Quiet){
        #Get account properties and add to list

         $temp = Invoke-Command -ComputerName $device -ScriptBlock {
             Get-LocalUser |  Select-Object -Property @{N="Computer"; E={$env:COMPUTERNAME}}, Name, Enabled, PasswordChangeableDate, PasswordExpires, UserMayChangePassword, PasswordRequired, PasswordLastSet, LastLogon
         }
         $Result.Add($temp)

    }else{
        $errors.Add($device)
        Write-Host "Cannot connect to $device, skipping." -ForegroundColor Red
    }
}

#display results
if ($displayOutputReport){
    if ($Result.Count -gt 0){
        $Result | Format-Table Computer,Name, Enabled, PasswordChangeableDate, PasswordExpires, UserMayChangePassword, PasswordRequired, PasswordLastSet, LastLogon -AutoSize
    }

    Write-Host ""

    if ($Errors.Count -gt 0){
        Write-Host "Errors:" -ForegroundColor Red
        $Errors
        Write-Host ""
    }
}

#export to CSV
$ScriptPath = $pwd.Path
$ReportDate = (Get-Date).ToString('MM-dd-yyyy hh-mm-ss tt')

if($Result.Count -gt 0){
    $reportFile = $ScriptPath + "\LocalUserAudit $ReportDate.csv"
    $Result | Select-Object Computer, Name, Enabled, PasswordChangeableDate, PasswordExpires, UserMayChangePassword, PasswordRequired, PasswordLastSet, LastLogon | Export-Csv $reportFile -NotypeInformation
    Write-Host "Report saved to $reportFile"
}

if ($Errors.Count -gt 0){
    $errorFile = $ScriptPath + "\LocalUserAudit ERRORS $ReportDate.txt"
    $Errors | Out-File $errorFile
    Write-Host "Errors saved to $errorFile"
}

【问题讨论】:

  • 如果你只是 Out-File 它,你会得到所有行吗?
  • 不走运。它垂直重复列标题 4 次(因为这是我的测试 list.txt 中的设备数)

标签: powershell


【解决方案1】:

改变

$Result.Add($temp)

$Result.AddRange(@($temp))

确保$Result 最终成为平面 对象集合。


至于你尝试了什么

$temp 可能是一个数组 对象,因为Get-LocalUser 可能报告多个 用户。

如果你将一个数组传递给List`1.Add(),它会作为一个整体添加到列表中,这不是你的意图。

因此,您最终得到了 数组 的列表,而不是 单个对象的平面列表。由于 array 没有您尝试选择的属性(NameEnabled、...),因此输出 CSV 中这些属性的列值最终为空。

相比之下,List`1.AddRange() 接受一个可枚举对象(数组隐式),并将每个枚举对象直接添加到列表中。

$temp 周围使用@(...) 可以防止Get-LocalUser 恰好返回一个 对象的情况 - @()array-subexpression operator,然后确保它被处理作为一个数组


更好的选择

如果您使用foreach 循环作为表达式,您可以让PowerShell 完成将所有输出对象收集到平面数组中的工作,这要归功于管道的自动集合枚举:

# Assign directly to $Result
$Result = foreach ($device in $devices){
    Write-Progress -Activity "Retrieving data from $device ($i of $length)" -percentComplete  ($i++*100/$length) -status Running

    if (Test-Connection -ComputerName $device -Count 1 -Quiet){
        #Get account properties and add to list

         # Simply pass the output of the Invoke-Command call through.
         Invoke-Command -ComputerName $device -ScriptBlock {
             Get-LocalUser |  Select-Object -Property @{N="Computer"; E={$env:COMPUTERNAME}}, Name, Enabled, PasswordChangeableDate, PasswordExpires, UserMayChangePassword, PasswordRequired, PasswordLastSet, LastLogon
         }

    }else{
        $errors.Add($device)
        Write-Host "Cannot connect to $device, skipping." -ForegroundColor Red
    }
}

这样不仅更方便,而且性能也更好。

注意:如果您需要确保$Result 始终是一个数组,请使用[array] $Result = foreach ...

【讨论】:

    【解决方案2】:

    更新: 我已经让它工作了,但是有人可以解释为什么它会起作用吗?当这将运行数千次时,我宁愿避免连接数组。

    我变了

    $Result = New-Object System.Collections.Generic.List[System.Object]
    

    $Result = @()
    

    $temp = Invoke-Command -ComputerName $device -ScriptBlock {
             Get-LocalUser |  Select-Object -Property @{N="Computer"; E={$env:COMPUTERNAME}}, Name, Enabled, PasswordChangeableDate, PasswordExpires, UserMayChangePassword, PasswordRequired, PasswordLastSet, LastLogon
         }
         $Result.Add($temp)
    

     $Result += Invoke-Command -ComputerName $device -ScriptBlock {
             Get-LocalUser |  Select-Object -Property @{N="Computer"; E={$env:COMPUTERNAME}}, Name, Enabled, PasswordChangeableDate, PasswordExpires, UserMayChangePassword, PasswordRequired, PasswordLastSet, LastLogon
        }
    

    【讨论】:

    • 至于为什么+=与一个数组一起工作:+,当应用于两个数组时执行element-wise连接,这样1,2 + 3, 4创建一个新的,平面数组1, 2, 3, 4.
    • 也就是说,出于性能原因,应该避免使用 += 迭代构建数组,并且简单地使用 foreach 循环让 PowerShell 收集数组中的所有输出既更方便又更快:请参阅已接受答案的底部部分,有关更多详细信息,包括性能测量,this answer
    • 一般来说,如果你想避免在内存中复制的惩罚,你可以将增量数据附加到文件中。
    猜你喜欢
    • 1970-01-01
    • 2014-06-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多