将Powershell输出附加到Excel文件

时间:2016-02-24 15:53:18

标签: excel powershell append output powershell-v4.0

我正在编写一个powershell脚本,它将从我正在工作的外部硬盘中收集一些信息(通过USB硬盘基座连接),我希望脚本将此信息添加到Excel文件中作为其中一个最后的步骤。我对Powershell有点熟悉,但与使用Powershell的Excel文件不太相似。

基本上,脚本需要能够确定工作表中的第一个空行,然后将一些信息粘贴到该行的A,B,C等列中。我找到了几种不同的方法,但由于某种原因,保存行号以添加信息的变量似乎总是空白。我不确定我做错了什么,所以任何建议都会非常感激。

它有点长,但这是我的脚本处于当前状态。底部的2个部分(标有“将信息附加到excel文档......”)是我找到的脚本示例(和源链接),据说可以做我正在尝试做的事情。

提前感谢您的帮助。

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 # http://stackoverflow.com/questions/8452408/using-powershell-to-append-a-table-to-the-end-of-an-excel-file-the-last-row $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 }

1 个答案:

答案 0 :(得分:2)

好的,CSV是可以的,但是在呈现数据或与他人共享时它只是不漂亮,所以我完全想要将数据转储到Excel。要将数据添加到Excel工作表,我强烈建议您启动一组对象,然后使用ConvertTo-CSV cmdlet Clip并粘贴到Excel中。

您并未真正说明要粘贴到Excel中的内容,但我将假设HostName,Serial#和用户。由于用户列表是一个数组,我将它加入一个字符串中,这样Excel就不会对它产生任何影响,并粘贴大量的空行。

所以让我们把这些信息放到一个对象中。有几种方法可以做到这一点,但我喜欢的方法(应该适用于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"

现在,如果您不希望每次我们将标题行粘贴到您的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)

现在,您可能需要更改自定义对象,但这应该是您想要的。如果您的脚本一次处理多个项目,您可以创建一个对象数组,并以这种方式将它们全部粘贴到Excel中。

编辑:好的,在哪里跳过标题,并将名称粘贴到自己的单元格中...

正如我所说,你会管道:

`$Record|ConvertTo-Csv -del "`t" -notype`

进入|Select -Skip 1。这可能会让我感到困惑,因为我将它传输到| Clip,但是如果你考虑管道是如何工作的,以及每个管道如何获取前一段的输出,那么你将ConvertTo-CSV传递给{ {1}}以便它跳过第一行(标题所在的位置),然后通过管道传递到Select -Skip 1将结果复制到剪贴板...当你将其分解时这是有意义的。该命令如下所示:

Clip

现在,为了将每个用户粘贴到他们自己的单元格中,您需要更加具体。您想要水平或垂直跨越细胞吗?如果你想横向跨越那么事情会变得复杂。基本上我们要做的是创建对象,然后使用$Record | ConvertTo-Csv -del "`t" -notype | Select -Skip 1 | Clip 向对象添加属性,每个用户一个。

Add-Member

这会创建一个这样的条目(标题留待参考,它们不会出现在Excel中):

#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

如果要垂直列出每个用户,可以为每个用户创建一个对象,但只在第一个用户上包含主机和S / N.

Hostname    Serial Number User0  User1 
--------    ------------- -----  ----- 
Computer001 42K6NNZ       TMTech Andrew

这会输出类似的内容(标题留待参考,它们不会出现在Excel中):

[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' = $_
}}