将 Powershell 输出附加到 Excel 文件
Append Powershell output to an Excel file
我正在编写一个 powershell 脚本,它将从我在工作时存档的外部硬盘驱动器(通过 USB 硬盘驱动器底座连接)收集一些信息,我想让脚本将此信息添加到 Excel 文件作为最后的步骤之一。我对 Powershell 比较熟悉,但不太了解如何使用 Powershell 处理 Excel 文件。
基本上,脚本需要能够确定 sheet 中的第一个空行,然后将一些信息粘贴到该行的 A、B、C 等列中。我找到了几种不同的方法来执行此操作,但出于某种原因,保存行号以添加信息的变量似乎始终为空白。我不确定我是否做错了什么,所以任何建议将不胜感激。
有点长,但这是我的脚本的当前状态。底部的 2 个部分(标有 "Append the info to an excel document...")是我发现的脚本示例(和源链接),据推测它们可以执行我正在尝试执行的操作。
在此先感谢您的帮助。
cls
($null = reg.exe unload HKLM\EXTERNAL) 2> $null
#IF ($AdminCred -eq $null) {New-Variable -Name AdminCred -Value (Get-Credential -UserName Administrator -Message "Enter password for the admin account") -Scope Global -Force}
$null = reg.exe load HKLM\EXTERNAL D:\Windows\System32\Config\SYSTEM
if ($LASTEXITCODE -eq 0) {
#Find the hostname
New-Variable -Name Hostname -Value ((Get-ItemProperty -Path HKLM:\EXTERNAL\ControlSet001\Control\ComputerName\ComputerName).ComputerName) -Scope Global -Force
[GC]::Collect()
IF ($LASTEXITCODE -ne 0) {Write-Host LastExitCode is $LASTEXITCODE}
$null = reg.exe unload HKLM\EXTERNAL
#Find the serial number
Clear-Variable -Name SerialNumber -Scope Global -Force
New-Variable -Name SerialNumber -Value (Read-Host "Please enter the HDD's serial number.") -Scope Global -Force
# I was originally trying to programatically gather the serial number of the HDD, but that is an issue for another post.
# $Disks = Get-WMIObject -class win32_PhysicalMedia
# $SerialNumber = foreach($Disk in $Disks) {IF ($Disk.SerialNumber -ne ' WD-WCC2EAV91692') {Write-Host $Disk.SerialNumber}}
#Find usernames
$Win7 = Test-Path D:\Users
$WinXP = Test-Path 'D:\Documents and Settings'
IF ($Win7 -eq 'True') {$Win7Users = (Get-ItemProperty -Path D:\Users\* -Exclude ADMINI~1,Administrator,Public,TEMP,UpdatusUser,'All Users',User).name}
IF ($WinXP -eq 'True') {$WinXPUsers = (Get-ItemProperty -Path 'D:\Documents and Settings\*' -Exclude ADMINI~1,Administrator,Public,TEMP,UpdatusUser,'All Users',User).name}
#Display Hostname and Usernames
Write-Host Hostname: -ForegroundColor Green
$hostname
Write-Host
Write-Host Serial Number: -ForegroundColor Green
$SerialNumber
Write-Host
Write-Host User List: -ForegroundColor Green
$Win7Users
$WinXPUsers
#Print the output
Out-File -FilePath C:\HDDinfo.txt -InputObject (New-Object -TypeName String -ArgumentList "Hostname:")
Out-File -FilePath C:\HDDinfo.txt -InputObject $Hostname -Append
Add-Content -Path C:\HDDinfo.txt `n
Out-File -FilePath C:\HDDinfo.txt -InputObject (New-Object -TypeName String -ArgumentList "Serial Number:") -Append
Out-File -FilePath C:\HDDinfo.txt -InputObject $SerialNumber -Append
Add-Content -Path C:\HDDinfo.txt `n
Out-File -FilePath C:\HDDinfo.txt -InputObject (New-Object -TypeName String -ArgumentList "User List:") -Append
Out-File -FilePath C:\HDDinfo.txt -InputObject $Win7Users -Append
Out-File -FilePath C:\HDDinfo.txt -InputObject $WinXPUsers -Append
Out-Printer \servername\printername -InputObject (Get-Content C:\HDDinfo.txt)
#Append the info to an Excel document - Possible method 1
#
$ExcelPath = "C:\HDDInfo.xlsx"
$xldown = -4121 # see: http://msdn.microsoft.com/en-us/library/bb241212(v=office.12).aspx
$xlup = -4162
$Excel = New-Object -ComObject Excel.Application
$Excel.Visible = $False
$ExcelWorkBook = $Excel.Workbooks.Open($ExcelPath)
$ExcelWorkSheet = $Excel.WorkSheets.item("Sheet1")
$ExcelWorkSheet.activate()
# Find the last used cell
{$lastRow = $ExcelWorkSheet.Cells.Range("A1048576").End($xlup).row
$nextRow = $lastRow + 1
$range = $ExcelWorkSheet.Range("A$nextRow")
$ExcelWorkSheet.Paste($range)}
# Append info to the spreadsheet
$ExcelWorkSheet.Cells.Item($row,1) = 'COLUMN 1 Text'
$ExcelWorkSheet.Cells.Item($row,2) = 'COLUMN 2 Text'
$ExcelWorkSheet.Cells.Item($row,3) = 'COLUMN 3 Text'
$ExcelWorkSheet.Cells.Item($row,4) = 'COLUMN 4 Text'
$ExcelWorkSheet.Cells.Item($row,5) = 'COLUMN 5 Text'
# Save and close
$ExcelWorkBook.Save()
$ExcelWorkBook.Close()
$Excel.Quit()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($Excel)
Stop-Process -Name EXCEL -Force
#Append the info to an Excel document - Possible method 2
# http://www.adamtheautomator.com/powershell-excel-worksheet/
$excel_file_path = 'C:\HDDInfo.xlsx'
# Instantiate the COM object
$Excel = New-Object -ComObject Excel.Application
$ExcelWorkBook = $Excel.Workbooks.Open($excel_file_path)
$ExcelWorkSheet = $Excel.WorkSheets.item("sheet1")
$ExcelWorkSheet.activate()
# Find the first row where the first 7 columns are empty
$row = ($ExcelWorkSheet.UsedRange.Rows | Where-Object { ($_.Value2 | Where-Object {$_ -eq $null}).Count -eq 7 } | select -first 1).Row
$ExcelWorkSheet.Cells.Item($row,1) = 'COLUMN 1 Text'
$ExcelWorkSheet.Cells.Item($row,2) = 'COLUMN 2 Text'
$ExcelWorkSheet.Cells.Item($row,3) = 'COLUMN 3 Text'
$ExcelWorkSheet.Cells.Item($row,4) = 'COLUMN 4 Text'
$ExcelWorkSheet.Cells.Item($row,5) = 'COLUMN 5 Text'
# Save and close
$ExcelWorkBook.Save()
$ExcelWorkBook.Close()
$Excel.Quit()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($Excel)
Stop-Process -Name EXCEL -Force
}
好吧,CSV 还可以,但是在呈现数据或与他人共享时它就是不漂亮,所以我完全想将数据转储到 Excel。要将数据添加到 Excel sheet,我强烈建议从 object 的数组开始,然后使用 ConvertTo-CSV
cmdlet、Clip
并粘贴进入 Excel.
您并没有真正说明要粘贴到 Excel 中的内容,但我将假设主机名、序列号和用户。由于用户列表是一个数组,我将把它连接成一个字符串,这样 Excel 就不会惊慌失措,并粘贴大量空行。
所以让我们将该信息放入 object。有几种方法可以做到这一点,但我更喜欢的方法(应该适用于 PS v3 及更高版本)是这样投射它:
$Record = [PSCustomObject]@{
'Hostname' = $Hostname
'Serial Number' = $Serialnumber
'Users' = $(If($XPUsers){$XPUsers}Else{$Win7Users}) -join ', '
}
现在,如果我们将其通过管道传输到 ConvertTo-Csv
并使用 -NoTypeInformation
开关将其制表符分隔,则结果如下所示:
PS C:\Temp> $Record|ConvertTo-Csv -del "`t" -notype
"Hostname" "Serial Number" "Users"
"Computer001" "42K6NNZ" "TMTech, Andrew"
现在,如果您不希望每次我们通过管道将 header 行粘贴到您的 Excel 文件中时 Select -Skip 1
即可解决该问题。但现在我们将包括它。这是我用来将该信息输入 Excel.
的代码
#Launch Excel
$XL = New-Object -ComObject Excel.Application
#Open the workbook
$WB = $XL.Workbooks.Open("C:\HDDInfo.xlsx")
#Activate Sheet1, pipe to Out-Null to avoid 'True' output to screen
$WB.Sheets.Item("Sheet1").Activate() | Out-Null
#Find first blank row #, and activate the first cell in that row
$FirstBlankRow = $($xl.ActiveSheet.UsedRange.Rows)[-1].Row + 1
$XL.ActiveSheet.Range("A$FirstBlankRow").Activate()
#Create PSObject with the properties that we want, convert it to a tab delimited CSV, and copy it to the clipboard
$Record = [PSCustomObject]@{
'Hostname' = $Hostname
'Serial Number' = $Serialnumber
'Users' = $(If($XPUsers){$XPUsers}Else{$Win7Users}) -join ', '
}
$Record | ConvertTo-Csv -Delimiter "`t" -NoTypeInformation | Clip
#Paste at the currently active cell
$XL.ActiveSheet.Paste() | Out-Null
# Save and close
$WB.Save() | Out-Null
$WB.Close() | Out-Null
$XL.Quit() | Out-Null
#Release ComObject
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($XL)
现在,您可能必须更改自定义设置 object,但这应该可以满足您的要求。如果您的脚本一次处理多个项目,您可以制作一个 object 的数组,并以这种方式将它们一次全部粘贴到 Excel 中。
编辑: 好的,在哪里跳过 header,并将名称粘贴到自己的单元格中...
就像我说的,你会管道:
`$Record|ConvertTo-Csv -del "`t" -notype`
变成|Select -Skip 1
。我猜这可能会令人困惑,因为然后我将它通过管道传输到 | Clip
,但是如果你考虑管道的工作方式,以及每个管道如何获取前一段的输出,那么你将 ConvertTo-CSV
传输到 Select -Skip 1
以便它跳过第一行(这是 header 所在的位置),然后通过管道传输到 Clip
以将结果复制到剪贴板......当你中断时它是有意义的它下来。该命令如下所示:
$Record | ConvertTo-Csv -del "`t" -notype | Select -Skip 1 | Clip
现在,要将每个用户粘贴到他们自己的单元格中,您需要更加具体。你想水平或垂直跨越单元格吗?如果你想水平跨越,那么事情就变得复杂了。基本上我们要做的是创建 object,然后使用 Add-Member
将属性添加到 object,每个用户一个。
#Create PSObject with the properties that we want, convert it to a tab delimited CSV, and copy it to the clipboard
$Record = [PSCustomObject]@{
'Hostname' = $Hostname
'Serial Number' = $Serialnumber
}
#Compile users, and add a member to the object for each user
[Array]$Users = If($WinXPUsers){$WinXPUsers}Else{$Win7Users}
For($i = 0; $i -lt $Users.Count; $i++){
$Record | Add-Member "User$i" $Users[$i]
}
$Record | ConvertTo-Csv -Delimiter "`t" -NoTypeInformation | Select -Skip 1 | Clip
这将创建一个这样的条目(header 留作参考,它们不会出现在 Excel 中):
Hostname Serial Number User0 User1
-------- ------------- ----- -----
Computer001 42K6NNZ TMTech Andrew
如果您想垂直列出每个用户,您可以为每个用户做一个 object,但只包括主机和第一个用户的 S/N。
[Array]$Users = If($WinXPUsers){$WinXPUsers}Else{$Win7Users}
[Array]$Record = [PSCustomObject]@{
'Hostname' = $Hostname
'Serial Number' = $Serialnumber
'Users' = $Users[0]
}
$Record += $Users | Select -Skip 1 | ForEach{[PSCustomObject]@{
'Hostname' = ''
'Serial Number' = ''
'Users' = $_
}}
这将输出如下内容(header 留作参考,它们不会出现在 Excel 中):
Hostname Serial Number Users
-------- ------------- -----
Computer001 42K6NNZ TMTech
Andrew
我正在编写一个 powershell 脚本,它将从我在工作时存档的外部硬盘驱动器(通过 USB 硬盘驱动器底座连接)收集一些信息,我想让脚本将此信息添加到 Excel 文件作为最后的步骤之一。我对 Powershell 比较熟悉,但不太了解如何使用 Powershell 处理 Excel 文件。
基本上,脚本需要能够确定 sheet 中的第一个空行,然后将一些信息粘贴到该行的 A、B、C 等列中。我找到了几种不同的方法来执行此操作,但出于某种原因,保存行号以添加信息的变量似乎始终为空白。我不确定我是否做错了什么,所以任何建议将不胜感激。
有点长,但这是我的脚本的当前状态。底部的 2 个部分(标有 "Append the info to an excel document...")是我发现的脚本示例(和源链接),据推测它们可以执行我正在尝试执行的操作。
在此先感谢您的帮助。
cls ($null = reg.exe unload HKLM\EXTERNAL) 2> $null #IF ($AdminCred -eq $null) {New-Variable -Name AdminCred -Value (Get-Credential -UserName Administrator -Message "Enter password for the admin account") -Scope Global -Force} $null = reg.exe load HKLM\EXTERNAL D:\Windows\System32\Config\SYSTEMif ($LASTEXITCODE -eq 0) { #Find the hostname New-Variable -Name Hostname -Value ((Get-ItemProperty -Path HKLM:\EXTERNAL\ControlSet001\Control\ComputerName\ComputerName).ComputerName) -Scope Global -Force [GC]::Collect() IF ($LASTEXITCODE -ne 0) {Write-Host LastExitCode is $LASTEXITCODE} $null = reg.exe unload HKLM\EXTERNAL
#Find the serial number Clear-Variable -Name SerialNumber -Scope Global -Force New-Variable -Name SerialNumber -Value (Read-Host "Please enter the HDD's serial number.") -Scope Global -Force # I was originally trying to programatically gather the serial number of the HDD, but that is an issue for another post. # $Disks = Get-WMIObject -class win32_PhysicalMedia # $SerialNumber = foreach($Disk in $Disks) {IF ($Disk.SerialNumber -ne ' WD-WCC2EAV91692') {Write-Host $Disk.SerialNumber}} #Find usernames $Win7 = Test-Path D:\Users $WinXP = Test-Path 'D:\Documents and Settings' IF ($Win7 -eq 'True') {$Win7Users = (Get-ItemProperty -Path D:\Users\* -Exclude ADMINI~1,Administrator,Public,TEMP,UpdatusUser,'All Users',User).name} IF ($WinXP -eq 'True') {$WinXPUsers = (Get-ItemProperty -Path 'D:\Documents and Settings\*' -Exclude ADMINI~1,Administrator,Public,TEMP,UpdatusUser,'All Users',User).name} #Display Hostname and Usernames Write-Host Hostname: -ForegroundColor Green $hostname Write-Host Write-Host Serial Number: -ForegroundColor Green $SerialNumber Write-Host Write-Host User List: -ForegroundColor Green $Win7Users $WinXPUsers #Print the output Out-File -FilePath C:\HDDinfo.txt -InputObject (New-Object -TypeName String -ArgumentList "Hostname:") Out-File -FilePath C:\HDDinfo.txt -InputObject $Hostname -Append Add-Content -Path C:\HDDinfo.txt `n Out-File -FilePath C:\HDDinfo.txt -InputObject (New-Object -TypeName String -ArgumentList "Serial Number:") -Append Out-File -FilePath C:\HDDinfo.txt -InputObject $SerialNumber -Append Add-Content -Path C:\HDDinfo.txt `n Out-File -FilePath C:\HDDinfo.txt -InputObject (New-Object -TypeName String -ArgumentList "User List:") -Append Out-File -FilePath C:\HDDinfo.txt -InputObject $Win7Users -Append Out-File -FilePath C:\HDDinfo.txt -InputObject $WinXPUsers -Append Out-Printer \servername\printername -InputObject (Get-Content C:\HDDinfo.txt) #Append the info to an Excel document - Possible method 1 # $ExcelPath = "C:\HDDInfo.xlsx" $xldown = -4121 # see: http://msdn.microsoft.com/en-us/library/bb241212(v=office.12).aspx $xlup = -4162 $Excel = New-Object -ComObject Excel.Application $Excel.Visible = $False $ExcelWorkBook = $Excel.Workbooks.Open($ExcelPath) $ExcelWorkSheet = $Excel.WorkSheets.item("Sheet1") $ExcelWorkSheet.activate() # Find the last used cell {$lastRow = $ExcelWorkSheet.Cells.Range("A1048576").End($xlup).row $nextRow = $lastRow + 1 $range = $ExcelWorkSheet.Range("A$nextRow") $ExcelWorkSheet.Paste($range)} # Append info to the spreadsheet $ExcelWorkSheet.Cells.Item($row,1) = 'COLUMN 1 Text' $ExcelWorkSheet.Cells.Item($row,2) = 'COLUMN 2 Text' $ExcelWorkSheet.Cells.Item($row,3) = 'COLUMN 3 Text' $ExcelWorkSheet.Cells.Item($row,4) = 'COLUMN 4 Text' $ExcelWorkSheet.Cells.Item($row,5) = 'COLUMN 5 Text' # Save and close $ExcelWorkBook.Save() $ExcelWorkBook.Close() $Excel.Quit() [System.Runtime.Interopservices.Marshal]::ReleaseComObject($Excel) Stop-Process -Name EXCEL -Force #Append the info to an Excel document - Possible method 2 # http://www.adamtheautomator.com/powershell-excel-worksheet/ $excel_file_path = 'C:\HDDInfo.xlsx' # Instantiate the COM object $Excel = New-Object -ComObject Excel.Application $ExcelWorkBook = $Excel.Workbooks.Open($excel_file_path) $ExcelWorkSheet = $Excel.WorkSheets.item("sheet1") $ExcelWorkSheet.activate() # Find the first row where the first 7 columns are empty $row = ($ExcelWorkSheet.UsedRange.Rows | Where-Object { ($_.Value2 | Where-Object {$_ -eq $null}).Count -eq 7 } | select -first 1).Row $ExcelWorkSheet.Cells.Item($row,1) = 'COLUMN 1 Text' $ExcelWorkSheet.Cells.Item($row,2) = 'COLUMN 2 Text' $ExcelWorkSheet.Cells.Item($row,3) = 'COLUMN 3 Text' $ExcelWorkSheet.Cells.Item($row,4) = 'COLUMN 4 Text' $ExcelWorkSheet.Cells.Item($row,5) = 'COLUMN 5 Text' # Save and close $ExcelWorkBook.Save() $ExcelWorkBook.Close() $Excel.Quit() [System.Runtime.Interopservices.Marshal]::ReleaseComObject($Excel) Stop-Process -Name EXCEL -Force }
好吧,CSV 还可以,但是在呈现数据或与他人共享时它就是不漂亮,所以我完全想将数据转储到 Excel。要将数据添加到 Excel sheet,我强烈建议从 object 的数组开始,然后使用 ConvertTo-CSV
cmdlet、Clip
并粘贴进入 Excel.
您并没有真正说明要粘贴到 Excel 中的内容,但我将假设主机名、序列号和用户。由于用户列表是一个数组,我将把它连接成一个字符串,这样 Excel 就不会惊慌失措,并粘贴大量空行。
所以让我们将该信息放入 object。有几种方法可以做到这一点,但我更喜欢的方法(应该适用于 PS v3 及更高版本)是这样投射它:
$Record = [PSCustomObject]@{
'Hostname' = $Hostname
'Serial Number' = $Serialnumber
'Users' = $(If($XPUsers){$XPUsers}Else{$Win7Users}) -join ', '
}
现在,如果我们将其通过管道传输到 ConvertTo-Csv
并使用 -NoTypeInformation
开关将其制表符分隔,则结果如下所示:
PS C:\Temp> $Record|ConvertTo-Csv -del "`t" -notype
"Hostname" "Serial Number" "Users"
"Computer001" "42K6NNZ" "TMTech, Andrew"
现在,如果您不希望每次我们通过管道将 header 行粘贴到您的 Excel 文件中时 Select -Skip 1
即可解决该问题。但现在我们将包括它。这是我用来将该信息输入 Excel.
#Launch Excel
$XL = New-Object -ComObject Excel.Application
#Open the workbook
$WB = $XL.Workbooks.Open("C:\HDDInfo.xlsx")
#Activate Sheet1, pipe to Out-Null to avoid 'True' output to screen
$WB.Sheets.Item("Sheet1").Activate() | Out-Null
#Find first blank row #, and activate the first cell in that row
$FirstBlankRow = $($xl.ActiveSheet.UsedRange.Rows)[-1].Row + 1
$XL.ActiveSheet.Range("A$FirstBlankRow").Activate()
#Create PSObject with the properties that we want, convert it to a tab delimited CSV, and copy it to the clipboard
$Record = [PSCustomObject]@{
'Hostname' = $Hostname
'Serial Number' = $Serialnumber
'Users' = $(If($XPUsers){$XPUsers}Else{$Win7Users}) -join ', '
}
$Record | ConvertTo-Csv -Delimiter "`t" -NoTypeInformation | Clip
#Paste at the currently active cell
$XL.ActiveSheet.Paste() | Out-Null
# Save and close
$WB.Save() | Out-Null
$WB.Close() | Out-Null
$XL.Quit() | Out-Null
#Release ComObject
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($XL)
现在,您可能必须更改自定义设置 object,但这应该可以满足您的要求。如果您的脚本一次处理多个项目,您可以制作一个 object 的数组,并以这种方式将它们一次全部粘贴到 Excel 中。
编辑: 好的,在哪里跳过 header,并将名称粘贴到自己的单元格中...
就像我说的,你会管道:
`$Record|ConvertTo-Csv -del "`t" -notype`
变成|Select -Skip 1
。我猜这可能会令人困惑,因为然后我将它通过管道传输到 | Clip
,但是如果你考虑管道的工作方式,以及每个管道如何获取前一段的输出,那么你将 ConvertTo-CSV
传输到 Select -Skip 1
以便它跳过第一行(这是 header 所在的位置),然后通过管道传输到 Clip
以将结果复制到剪贴板......当你中断时它是有意义的它下来。该命令如下所示:
$Record | ConvertTo-Csv -del "`t" -notype | Select -Skip 1 | Clip
现在,要将每个用户粘贴到他们自己的单元格中,您需要更加具体。你想水平或垂直跨越单元格吗?如果你想水平跨越,那么事情就变得复杂了。基本上我们要做的是创建 object,然后使用 Add-Member
将属性添加到 object,每个用户一个。
#Create PSObject with the properties that we want, convert it to a tab delimited CSV, and copy it to the clipboard
$Record = [PSCustomObject]@{
'Hostname' = $Hostname
'Serial Number' = $Serialnumber
}
#Compile users, and add a member to the object for each user
[Array]$Users = If($WinXPUsers){$WinXPUsers}Else{$Win7Users}
For($i = 0; $i -lt $Users.Count; $i++){
$Record | Add-Member "User$i" $Users[$i]
}
$Record | ConvertTo-Csv -Delimiter "`t" -NoTypeInformation | Select -Skip 1 | Clip
这将创建一个这样的条目(header 留作参考,它们不会出现在 Excel 中):
Hostname Serial Number User0 User1
-------- ------------- ----- -----
Computer001 42K6NNZ TMTech Andrew
如果您想垂直列出每个用户,您可以为每个用户做一个 object,但只包括主机和第一个用户的 S/N。
[Array]$Users = If($WinXPUsers){$WinXPUsers}Else{$Win7Users}
[Array]$Record = [PSCustomObject]@{
'Hostname' = $Hostname
'Serial Number' = $Serialnumber
'Users' = $Users[0]
}
$Record += $Users | Select -Skip 1 | ForEach{[PSCustomObject]@{
'Hostname' = ''
'Serial Number' = ''
'Users' = $_
}}
这将输出如下内容(header 留作参考,它们不会出现在 Excel 中):
Hostname Serial Number Users
-------- ------------- -----
Computer001 42K6NNZ TMTech
Andrew