简体   繁体   English

Append Powershell output 到 Excel 文件

[英]Append Powershell output to an Excel file

I am writing a powershell script that will gather some information from external HDDs I am archiving at work (connected via a USB HDD dock), and I would like to have the script add this info to an Excel file as one of the last steps.我正在编写一个 powershell 脚本,它将从我在工作时存档的外部硬盘驱动器(通过 USB 硬盘驱动器底座连接)收集一些信息,我想让脚本将此信息添加到 Excel 文件作为最后的步骤之一。 I am somewhat familiar with Powershell, but not so much on working with Excel files with Powershell.我对 Powershell 比较熟悉,但对使用 Powershell 处理 Excel 文件不是很熟悉。

Basically, the script needs to be able to determine the first empty row in the sheet, and then paste some info into columns A, B, C, etc of that row.基本上,脚本需要能够确定工作表中的第一个空行,然后将一些信息粘贴到该行的 A、B、C 等列中。 I have found a couple different ways of doing this, but for some reason the variable that would hold the row number to add info to always seems to be blank.我找到了几种不同的方法来执行此操作,但由于某种原因,保存行号以添加信息的变量似乎总是空白。 I'm not sure if I'm doing something wrong, so any advice would be much appreciated.我不确定我是否做错了什么,所以任何建议将不胜感激。

It's a bit long, but here is my script in it's current state. The bottom 2 sections (labeled with "Append the info to an excel document...") are the script samples I have found (and source links) that supposedly does what I'm trying to do.它有点长,但这是我的脚本,它是当前的 state。底部的 2 个部分(标有“将信息附加到 excel 文档...”)是我发现的脚本示例(和源链接),据说可以我正在尝试做什么。

Thanks in advance for the help.先谢谢您的帮助。

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 }

Ok, CSV is ok, but when presenting data, or sharing with others it's just not pretty, so I totally get wanting to dump the data to Excel. 好的,CSV可以,但是在显示数据或与其他人共享时,它并不漂亮,所以我完全想将数据转储到Excel。 To add data to an Excel sheet I strongly suggest having an array of objects to start, and then using the ConvertTo-CSV cmdlet, Clip , and pasting into Excel. 要将数据添加到Excel工作表中,我强烈建议先启动一系列对象,然后使用ConvertTo-CSV cmdlet, Clip并将其粘贴到Excel中。

You don't really state what you want to paste into Excel, but I'm going to assume HostName, Serial#, and the users. 您并没有真正声明要粘贴到Excel中的内容,但是我将假设HostName,Serial#和用户。 Since the user list is an array, I'll join it into a string so that Excel doesn't freak out about it, and paste a ton of empty rows. 由于用户列表是一个数组,因此我将其连接到字符串中,以使Excel不会迷恋它,并粘贴大量的空行。

So let's get that info into an object. 因此,让我们将该信息放入对象中。 There's a few ways to do that, but the one that I prefer (should work on PS v3 and higher) is to cast it as such: 有几种方法可以做到这一点,但是我更喜欢的一种方法(应该在PS v3及更高版本上可以使用)是这样转换的:

$Record = [PSCustomObject]@{
    'Hostname' = $Hostname
    'Serial Number' = $Serialnumber
    'Users' =  $(If($XPUsers){$XPUsers}Else{$Win7Users}) -join ', '
}

Now if we pipe that to ConvertTo-Csv and make it tab delimited with the -NoTypeInformation switch it comes out something like this: 现在,如果我们将其通过管道传输到ConvertTo-Csv并使用-NoTypeInformation开关将其制表符分隔,则会出现以下内容:

PS C:\Temp> $Record|ConvertTo-Csv -del "`t" -notype
"Hostname"  "Serial Number" "Users"
"Computer001"   "42K6NNZ"   "TMTech, Andrew"

Now if you don't want the header row pasted into your Excel file each time we pipe that to Select -Skip 1 and that solves that. 现在,如果您不希望每次我们将其通过管道传递给Select -Skip 1都可以将标题行粘贴到Excel文件中,那么可以解决该问题。 But for now we'll include it. 但是现在我们将其包括在内。 Here is the code that I would use to get that info into Excel. 这是我用来将信息导入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)

Now, you may have to change the custom object, but that should do what you want. 现在,您可能必须更改自定义对象,但这应该可以完成您想要的操作。 If your script processes more than one item at a time, you can make an array of objects, and paste them all into Excel at once this way. 如果您的脚本一次处理多个项目,则可以制作一个对象数组,然后将它们一次全部粘贴到Excel中。

Edit: Ok, where to skip the header, and pasting names into their own cells... 编辑:好的,在哪里跳过标题,并将名称粘贴到自己的单元格中...

As I said, you would pipe: 就像我说的,您可以通过管道:

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

into |Select -Skip 1 . 进入|Select -Skip 1 This could be confusing I guess, because then I piped it into | Clip 我猜这可能会令人困惑,因为随后我将其传送到| Clip | Clip , but if you consider how the pipeline works, and how each pipe takes the output from the previous segment, then you pipe ConvertTo-CSV into Select -Skip 1 so that it skips the first line (which is where the headers are), and then pipe to Clip to copy the result to the clipboard... It makes sense when you break it down. | Clip ,但是如果您考虑管道的工作方式,以及每个管道如何从上一段获取输出,则将ConvertTo-CSV管道输送到Select -Skip 1以便跳过第一行(标头所在的位置),然后通过管道传递给Clip ,将结果复制到剪贴板上。 The command would look like this: 该命令将如下所示:

$Record | ConvertTo-Csv -del "`t" -notype | Select -Skip 1 | Clip

Now, for pasting each user into their own cell, you need to be more specific. 现在,要将每个用户粘贴到自己的单元格中,您需要更加具体。 Do you want to span cells horizontally, or vertically? 您要水平或垂直跨单元格吗? If you want to span horizontally then things get complicated. 如果要水平跨度,则事情会变得复杂。 Basically what we would do is create the object, and then use Add-Member to add properties to the object, one for each user. 基本上,我们要做的是创建对象,然后使用Add-Member向对象添加属性,每个用户一个。

#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

That would create an entry like this (headers left in for reference, they wouldn't appear in Excel): 这样会创建一个这样的条目(保留标题以供参考,它们不会出现在Excel中):

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

If you want to list each user vertically you could do something like making an object for each user, but only including the Host and S/N on the first one. 如果要垂直列出每个用户,则可以执行为每个用户创建对象的操作,但只在第一个用户中包含主机和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' = $_
}}

This would output something like this (headers left in for reference, they wouldn't appear in Excel): 这将输出类似以下内容(标题留作参考,它们不会出现在Excel中):

Hostname    Serial Number Users 
--------    ------------- ----- 
Computer001 42K6NNZ       TMTech
                          Andrew

if you use this for your excel object it will append for you(make sure your row is = 1).如果你将它用于你的 excel object 它将为你 append(确保你的行 = 1)。

do {
        $row++
    } until ($excel.cells.item($row,$column).text -eq "")

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM