2016-02-24 243 views
1

我正在写一个powershell脚本,它将收集我在工作中存档的外部硬盘(通过USB HDD底座连接)的一些信息,并且我希望脚本将此信息添加到作为最后一个步骤之一的Excel文件。我对Powershell有些熟悉,但与使用Powershell处理Excel文件没有多大关系。将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

你需要使用Excel ComObject?你可以使用['Export-Csv'](https://technet.microsoft.com/en-us/library/hh849932.aspx)cmdlet吗? CSV文件可以通过Excel打开。 – romellem

+0

与romellem同时发生。我在这里看不到任何真正需要com对象的东西。只是基本的字段填充。使用对象'Export-CSV'会使这个过程变得更容易。 – Matt

+0

嗯,我宁愿将该文件视为.xslx格式,因为除我以外的人(以及IT以外的人)都可能打开该文件。我可以导出到一个csv,但我仍然需要有xlsx文件,所以我需要定期手动传输数据。它会完成工作,但如果可能的话,我想避免额外的手动操作。 – Andrew

回答

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管道Select -Skip 1以便跳过第一行(这是标题所在的位置),然后管道到Clip将结果复制到剪贴板中。当它分解时它是有意义的。命令如下所示:

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

现在,为了将每个用户粘贴到他们自己的单元格中,您需要更具体。你想要水平或垂直跨越单元格吗?如果你想横向跨越,那么事情就会变得复杂。基本上我们要做的是创建对象,然后使用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 

这将创建一个条目是这样的(头留在作参考,他们将不会出现在Excel中):如果要列出每个用户垂直,你可以做类似

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

为每个用户制作一个对象,但仅在第一个用户中包含主机和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' = $_ 
}} 

这将输出像这样(左参考报头,他们将不会出现在Excel中):

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

谢谢,我会试一试。还有一个关于csv主题的想法 - 它是否会将脚本附加到csv以简化起见,然后以编程方式将csv的副本保存为其他人的xlsx文件?每次转换csv时,我都可能只覆盖xlsx文件。 – Andrew

+0

还有一件事 - 我需要添加“选择-Skip 1”来粘贴数据而不是头文件?我尝试了几个地方,但似乎没有工作。另外,是否有办法将每个名称粘贴到它自己的单元格中?我不确定哪个更好,但它目前正在将整个名单粘贴到一个单元格中。 – Andrew

+0

至于写入CSV然后转换为XLSx,您必须以任何方式打开Excel ComObject,因此只需将Excel文件更新到我就可以了。我更新了我的答案,希望能解决您的其他问题。 – TheMadTechnician