Powershell - 正确导出 CSV 文件
Powershell - Export CSV file correctly
我希望有人能帮助我解决这个问题。我们想看看哪些计算机有 HDD 和 SDD。我有 excel.csv 台计算机。我进口电脑。但是当我导出它们时,我从来没有看到 csv 或其不完整。你能告诉我脚本的哪一部分不正确吗?谢谢
$computers = Import-csv -path "C:\Temp\MediaType\Computers.csv"
foreach ($computer in $computers) {
Write-Host "`nPulling Physical Drive(s) for $computer"
if((Test-Connection -BufferSize 32 -Count 1 -ComputerName $computer -Quiet)){
Invoke-Command -ComputerName $computer -ScriptBlock {
Get-WmiObject -Class MSFT_PhysicalDisk -Namespace root\Microsoft\Windows\Storage | Select-Object sort -Property PSComputerName, Model, SerialNumber, MediaType
Export-Csv C:\Temp\devices.csv
}
}
}
更新:2021 年 11 月 11 日
谢谢大家的帮助
这个脚本对我有用:
$ExportTo = "C:\Temp\devices.csv"
$computers = Import-csv -path "C:\Temp\Computers.csv"
{} | Select "ComputerName", "Status", "Model", "SerialNumber", "MediaType" | Export-Csv $ExportTo
$data = Import-csv -path $ExportTo
foreach ($computer in $computers) {
$Online = Test-Connection -BufferSize 32 -Count 1 -ComputerName $computer.computer -Quiet
if ($Online) {
Write-Host $computer.computer " is Online"
$OutputMessage = Get-CimInstance -ClassName MSFT_PhysicalDisk -Namespace root\Microsoft\Windows\Storage -ComputerName $computer.computer | Select-Object -Property PSComputerName,@{N='Status';E={'Online'}}, Model, SerialNumber, MediaType
$data.ComputerName = $computer.computer
$data.Status = $OutputMessage.Status
$data.Model = $OutputMessage.Model
$data.SerialNumber = $OutputMessage.SerialNumber
$data.MediaType = $OutputMessage.MediaType
$data | Export-Csv -Path $ExportTo -Append -NoTypeInformation
} else {
Write-Host $computer.computer " is Offline"
$data.ComputerName = $computer.computer
$data.Status = "Offline"
$data.Model = ""
$data.SerialNumber = ""
$data.MediaType = ""
$data | Export-Csv -Path $ExportTo -Append -NoTypeInformation
}
}
继续我的评论。 . .照原样,您会将结果导出到远程机器。那就是如果它 管道 正确。您目前在 Export-Csv
.
之前缺少管道 (|
)
此外,不需要调用该命令,因为 Get-WMIObject
有一个用于远程计算机的参数:-ComputerName
。它也是一个已弃用的 cmdlet,已被替换为 Get-CimInstance.
$ExportTo = "C:\Temp\devices.csv"
$computers = Import-csv -path "C:\Temp\MediaType\Computers.csv"
foreach ($computer in $computers)
{
Write-Host "`nPulling Physical Drive(s) for $computer"
if (Test-Connection -BufferSize 32 -Count 1 -ComputerName $computer -Quiet) {
Get-CimInstance -ClassName MSFT_PhysicalDisk -Namespace root\Microsoft\Windows\Storage -ComputerName $computer |
Select-Object -Property PSComputerName, Model, SerialNumber, MediaType |
Export-Csv -Path $ExportTo -Append -NoTypeInformation
}
}
旁注:Get-CimInstance
接受字符串数组,这意味着您可以将整个 $Computers
传递给它。这应该允许它在并行中执行查询,而不是串行(一次一个) :
$ExportTo = "C:\Temp\devices.csv"
$computers = Import-csv -path "C:\Temp\MediaType\Computers.csv"
Get-CimInstance -ClassName MSFT_PhysicalDisk -Namespace root\Microsoft\Windows\Storage -ComputerName $computers -ErrorAction SilentlyContinue |
Select-Object -Property PSComputerName, Model, SerialNumber, MediaType |
Export-Csv -Path $ExportTo -Append -NoTypeInformation
一次执行一个查询并不一定意味着它不好。实际上,您可以更好地控制脚本的 流程控制 。
编辑:
跟进您的评论...您不再使用 if
语句在连接之前检查计算机是否在线。因此,假设您保留 if
语句,并添加一个 else
条件,您可以创建一个计算的 属性 以添加另一个 属性 以导出 Status 。然后,你可以给它传递一个值Online
,或者Offline
,这取决于机器是否在线:
$ExportTo = "C:\Temp\devices.csv"
$computers = Import-csv -path "C:\Temp\MediaType\Computers.csv"
foreach ($computer in $computers)
{
if (Test-Connection -BufferSize 32 -Count 1 -ComputerName $computer -Quiet) {
Write-Host -Object "`nPulling Physical Drive(s) for $computer"
Get-CimInstance -ClassName MSFT_PhysicalDisk -Namespace root\Microsoft\Windows\Storage -ComputerName $computer |
Select-Object -Property PSComputerName,@{N='Status';E={'Online'}}, Model, SerialNumber, MediaType |
Export-Csv -Path $ExportTo -Append -NoTypeInformation -Force
}
else {
Write-Host -Object "`n$Computer is Offline"
[PSCustomObject]@{PSComputerName=$Computer;Status='Offline'} | Export-Csv -Path $ExportTo -Append -Force
}
}
还有:
- 永远记住,即使你能ping通一台机器,并不意味着你能连接上它。
- 这可以通过使用 CIM 会话 或 PSSession 来缓解,具体取决于您使用的命令类型重新 运行.
具体回答问题:
如何正确导出 CSV 文件(使用 Export-Csv
)?
您可能想阅读有关 PowerShell pipelines and PowerShell cmdlets 的内容。
基本上,cmdlet 是参与 PowerShell 管道语义的单个命令。一个写得很好的 cmdlet 是 implemented for the Middle of a Pipeline,这意味着它处理(“streams”)从前一个 cmdlet 接收到的每个单独的项目,并立即将它传递给下一个 cmdlet(类似于如何项目在装配线上处理,您可以将每个装配站作为一个 cmdlet 进行比较)。
为了更好地展示这一点,我创建了一个更简单的 minimal, complete and verifiable example (MVCE) 并替换了您的远程命令 (Invoke-Command ...
),它只是一个伪造的 [pscustomobject]@{ ... }
object.
有了那个;
- 我使用了
Get-Content
rather then Import-Csv
as your example suggest that Computers.csv
is actually a text file which list of computers and not a Csv
file,这需要一个(例如 Name
)header 并相应地使用这个 属性(例如 $Computer.Name
)。
- 为了执行管道 advantage/understanding,我还使用了
ForEach-Object
cmdlet rather than the foreach
statement,它 通常 被认为更快,但这可能 不是 这里的情况对于 foreach
语句,需要将所有 $Computers
预加载到内存中,在内存中,编写良好的管道将 立即 开始处理每个项目(在您的情况下发生在远程计算机上),同时仍然从文件中检索下一个计算机名称。
现在,回到问题“如何正确导出 CSV 文件”,这样可以更好地理解管道,您可以将 Export-Csv
放在 foreach 循环中::
Get-Content .\Computers.txt |ForEach-Object {
[pscustomobject]@{
PSComputerName = $_
Model = "Model"
SerialNumber = '{0:000000}' -f (Get-Random 999999)
MediaType = "MydiaType"
} |Export-Csv .\Devices.csv -Append
}
正如 @lit 所评论的那样,这将需要 -Append
开关,这可能不是您每次重新运行脚本时所需要的,因为这会将结果附加到 .\Devices.csv
文件中。
相反,你可能真的想这样做:
Get-Content .\Computers.txt |ForEach-Object {
[pscustomobject]@{
PSComputerName = $_
Model = "Model"
SerialNumber = '{0:000000}' -f (Get-Random 999999)
MediaType = "MydiaType"
}
} |Export-Csv .\Devices.csv
注意区别:Export-Csv
放在循环外,-Append
开关被移除。
说明
与例如ForEach-Object
cmdlet, the Export-Csv
cmdlet has internally Begin
, Process
and End
blocks.
- 在
Begin
块(管道启动时运行)中,Export-Csv
cmdlet 准备 csv
文件,其中包含 header 行等。和覆盖任何现有文件。
- 在
Process
块(针对从管道接收到的每个项目运行)中,它 将每一行(数据记录)追加 到文件。
我希望有人能帮助我解决这个问题。我们想看看哪些计算机有 HDD 和 SDD。我有 excel.csv 台计算机。我进口电脑。但是当我导出它们时,我从来没有看到 csv 或其不完整。你能告诉我脚本的哪一部分不正确吗?谢谢
$computers = Import-csv -path "C:\Temp\MediaType\Computers.csv"
foreach ($computer in $computers) {
Write-Host "`nPulling Physical Drive(s) for $computer"
if((Test-Connection -BufferSize 32 -Count 1 -ComputerName $computer -Quiet)){
Invoke-Command -ComputerName $computer -ScriptBlock {
Get-WmiObject -Class MSFT_PhysicalDisk -Namespace root\Microsoft\Windows\Storage | Select-Object sort -Property PSComputerName, Model, SerialNumber, MediaType
Export-Csv C:\Temp\devices.csv
}
}
}
更新:2021 年 11 月 11 日 谢谢大家的帮助 这个脚本对我有用:
$ExportTo = "C:\Temp\devices.csv"
$computers = Import-csv -path "C:\Temp\Computers.csv"
{} | Select "ComputerName", "Status", "Model", "SerialNumber", "MediaType" | Export-Csv $ExportTo
$data = Import-csv -path $ExportTo
foreach ($computer in $computers) {
$Online = Test-Connection -BufferSize 32 -Count 1 -ComputerName $computer.computer -Quiet
if ($Online) {
Write-Host $computer.computer " is Online"
$OutputMessage = Get-CimInstance -ClassName MSFT_PhysicalDisk -Namespace root\Microsoft\Windows\Storage -ComputerName $computer.computer | Select-Object -Property PSComputerName,@{N='Status';E={'Online'}}, Model, SerialNumber, MediaType
$data.ComputerName = $computer.computer
$data.Status = $OutputMessage.Status
$data.Model = $OutputMessage.Model
$data.SerialNumber = $OutputMessage.SerialNumber
$data.MediaType = $OutputMessage.MediaType
$data | Export-Csv -Path $ExportTo -Append -NoTypeInformation
} else {
Write-Host $computer.computer " is Offline"
$data.ComputerName = $computer.computer
$data.Status = "Offline"
$data.Model = ""
$data.SerialNumber = ""
$data.MediaType = ""
$data | Export-Csv -Path $ExportTo -Append -NoTypeInformation
}
}
继续我的评论。 . .照原样,您会将结果导出到远程机器。那就是如果它 管道 正确。您目前在 Export-Csv
.
|
)
此外,不需要调用该命令,因为 Get-WMIObject
有一个用于远程计算机的参数:-ComputerName
。它也是一个已弃用的 cmdlet,已被替换为 Get-CimInstance.
$ExportTo = "C:\Temp\devices.csv"
$computers = Import-csv -path "C:\Temp\MediaType\Computers.csv"
foreach ($computer in $computers)
{
Write-Host "`nPulling Physical Drive(s) for $computer"
if (Test-Connection -BufferSize 32 -Count 1 -ComputerName $computer -Quiet) {
Get-CimInstance -ClassName MSFT_PhysicalDisk -Namespace root\Microsoft\Windows\Storage -ComputerName $computer |
Select-Object -Property PSComputerName, Model, SerialNumber, MediaType |
Export-Csv -Path $ExportTo -Append -NoTypeInformation
}
}
旁注:Get-CimInstance
接受字符串数组,这意味着您可以将整个 $Computers
传递给它。这应该允许它在并行中执行查询,而不是串行(一次一个) :
$ExportTo = "C:\Temp\devices.csv"
$computers = Import-csv -path "C:\Temp\MediaType\Computers.csv"
Get-CimInstance -ClassName MSFT_PhysicalDisk -Namespace root\Microsoft\Windows\Storage -ComputerName $computers -ErrorAction SilentlyContinue |
Select-Object -Property PSComputerName, Model, SerialNumber, MediaType |
Export-Csv -Path $ExportTo -Append -NoTypeInformation
一次执行一个查询并不一定意味着它不好。实际上,您可以更好地控制脚本的 流程控制 。
编辑:
跟进您的评论...您不再使用 if
语句在连接之前检查计算机是否在线。因此,假设您保留 if
语句,并添加一个 else
条件,您可以创建一个计算的 属性 以添加另一个 属性 以导出 Status 。然后,你可以给它传递一个值Online
,或者Offline
,这取决于机器是否在线:
$ExportTo = "C:\Temp\devices.csv"
$computers = Import-csv -path "C:\Temp\MediaType\Computers.csv"
foreach ($computer in $computers)
{
if (Test-Connection -BufferSize 32 -Count 1 -ComputerName $computer -Quiet) {
Write-Host -Object "`nPulling Physical Drive(s) for $computer"
Get-CimInstance -ClassName MSFT_PhysicalDisk -Namespace root\Microsoft\Windows\Storage -ComputerName $computer |
Select-Object -Property PSComputerName,@{N='Status';E={'Online'}}, Model, SerialNumber, MediaType |
Export-Csv -Path $ExportTo -Append -NoTypeInformation -Force
}
else {
Write-Host -Object "`n$Computer is Offline"
[PSCustomObject]@{PSComputerName=$Computer;Status='Offline'} | Export-Csv -Path $ExportTo -Append -Force
}
}
还有:
- 永远记住,即使你能ping通一台机器,并不意味着你能连接上它。
- 这可以通过使用 CIM 会话 或 PSSession 来缓解,具体取决于您使用的命令类型重新 运行.
具体回答问题:
如何正确导出 CSV 文件(使用 Export-Csv
)?
您可能想阅读有关 PowerShell pipelines and PowerShell cmdlets 的内容。
基本上,cmdlet 是参与 PowerShell 管道语义的单个命令。一个写得很好的 cmdlet 是 implemented for the Middle of a Pipeline,这意味着它处理(“streams”)从前一个 cmdlet 接收到的每个单独的项目,并立即将它传递给下一个 cmdlet(类似于如何项目在装配线上处理,您可以将每个装配站作为一个 cmdlet 进行比较)。
为了更好地展示这一点,我创建了一个更简单的 minimal, complete and verifiable example (MVCE) 并替换了您的远程命令 (Invoke-Command ...
),它只是一个伪造的 [pscustomobject]@{ ... }
object.
有了那个;
- 我使用了
Get-Content
rather thenImport-Csv
as your example suggest thatComputers.csv
is actually a text file which list of computers and not aCsv
file,这需要一个(例如Name
)header 并相应地使用这个 属性(例如$Computer.Name
)。 - 为了执行管道 advantage/understanding,我还使用了
ForEach-Object
cmdlet rather than theforeach
statement,它 通常 被认为更快,但这可能 不是 这里的情况对于foreach
语句,需要将所有$Computers
预加载到内存中,在内存中,编写良好的管道将 立即 开始处理每个项目(在您的情况下发生在远程计算机上),同时仍然从文件中检索下一个计算机名称。
现在,回到问题“如何正确导出 CSV 文件”,这样可以更好地理解管道,您可以将 Export-Csv
放在 foreach 循环中::
Get-Content .\Computers.txt |ForEach-Object {
[pscustomobject]@{
PSComputerName = $_
Model = "Model"
SerialNumber = '{0:000000}' -f (Get-Random 999999)
MediaType = "MydiaType"
} |Export-Csv .\Devices.csv -Append
}
正如 @lit 所评论的那样,这将需要 -Append
开关,这可能不是您每次重新运行脚本时所需要的,因为这会将结果附加到 .\Devices.csv
文件中。
相反,你可能真的想这样做:
Get-Content .\Computers.txt |ForEach-Object {
[pscustomobject]@{
PSComputerName = $_
Model = "Model"
SerialNumber = '{0:000000}' -f (Get-Random 999999)
MediaType = "MydiaType"
}
} |Export-Csv .\Devices.csv
注意区别:Export-Csv
放在循环外,-Append
开关被移除。
说明
与例如ForEach-Object
cmdlet, the Export-Csv
cmdlet has internally Begin
, Process
and End
blocks.
- 在
Begin
块(管道启动时运行)中,Export-Csv
cmdlet 准备csv
文件,其中包含 header 行等。和覆盖任何现有文件。 - 在
Process
块(针对从管道接收到的每个项目运行)中,它 将每一行(数据记录)追加 到文件。