I have a script that places everything nicely into a spread sheet. The problem is, I need it to export as a csv file instead. All the foreach loops are completely baffling me here as far as where to put the export csv functions in the script. If someone could could school me on how to get the fields into a csv file, It would be greatly appreciated.
$date = 0
$date = get-date -format "yyyy-MMM-dd-hhmm"
$date
#New Excel Application
$Excel = New-Object -Com Excel.Application
$Excel.visible = $False
# Create 1 worksheets
$Excel = $Excel.Workbooks.Add()
# Assign each worksheet to a variable and
# name the worksheet.
$Sheet1 = $Excel.Worksheets.Item(1)
$Sheet1.Name = "HH_SERVERS"
#Create Heading for General Sheet
$Sheet1.Cells.Item(1, 1) = "Machine_Name"
$Sheet1.Cells.Item(1, 2) = "OS"
$Sheet1.Cells.Item(1, 3) = "Software"
$Sheet1.Cells.Item(1, 4) = "Vendor"
$Sheet1.Cells.Item(1, 5) = "Version"
$colSheets = ($Sheet1)
foreach ($colorItem in $colSheets)
{
$intRow = 2
$intRowDisk = 2
$intRowSoft = 2
$intRowNet = 2
$WorkBook = $colorItem.UsedRange
$WorkBook.Interior.ColorIndex = 20
$WorkBook.Font.ColorIndex = 11
$WorkBook.Font.Bold = $True
}
#Auto Fit all sheets in the Workbook
foreach ($colorItem in $colSheets)
{
$WorkBook = $colorItem.UsedRange
$WorkBook.EntireColumn.AutoFit()
clear
}
$Servers = get-content "c:\temp\HH_Servers.txt"
foreach ($Server in $Servers)
{
$GenItems2 = gwmi Win32_OperatingSystem -Comp $Server
$Software = gwmi Win32_Product -Comp $Server
# Populate General Sheet(1) with information
foreach ($objItem in $GenItems2)
{
$Sheet1.Cells.Item($intRow, 2) = $objItem.Caption
}
#Populate Software Sheet
foreach ($objItem in $Software)
{
$Sheet1.Cells.Item($intRowSoft, 1) = $Server
$Sheet1.Cells.Item($intRowSoft, 3) = $objItem.Name
$Sheet1.Cells.Item($intRowSoft, 4) = $objItem.Vendor
$Sheet1.Cells.Item($intRowSoft, 5) = $objItem.Version
$intRowSoft = $intRowSoft + 1
}
}
$outputfile = "c:\temp\" + $date.toString() + "-HH_Server_Software"
$Excel.SaveAs($outputfile)
$Excel.Close()
Write-Host "*******************************" -ForegroundColor Green
Write-Host "The Report has been completed." -ForeGroundColor Green
Write-Host "*******************************" -ForegroundColor Green
# ========================================================================
# END of Script
# ==================
You can't save an entire workbook as CSV. You need to save the individual worksheet instead. The file format value for CSV is 6 (don't remember where I found that out though):
$xlCSV = 6
$outputfile = "c:\temp\" + $date.toString() + "-HH_Server_Software.csv"
$Sheet1.SaveAs($outputfile, $xlCSV)
(Tested on Windows 7 with Excel 2013.)
Thanks to #Matt for a comment with a link to the XLFileFormat Enumerations.
Related
I'm trying to create a script that pulls print device from a group of servers housed in a text file. The script works fine except it only pulls one device from one server then the script completes. I'm trying to get this to work then loop in another command to combine all the data from all the sheets and look for dissimilarities between the server(s).
clear-host
# Get list of servers from text file
$sites = Get-Content -Path "User\user$\user\Documents\Working Folder\2132023\test.txt"
$counter = 4
# And here
foreach ($site in $sites) {
$result = Get-Printer -ComputerName $site | Select Name, DriverName, PortName, ShareName
#Create an Excel object
$ExcelObj = New-Object -comobject Excel.Application
$ExcelObj.Visible = $true
# Add a workbook
$ExcelWorkBook = $ExcelObj.Workbooks.Add()
$ExcelWorkSheet = $ExcelWorkBook.Worksheets.Item(1)
# Rename the worksheet
$ExcelWorkSheet.Name = $site
# Fill in the head of the table
$ExcelWorkSheet.Cells.Item(1, 1) = 'Device Name'
$ExcelWorkSheet.Cells.Item(1, 2) = 'Driver Name'
$ExcelWorkSheet.Cells.Item(1, 3) = 'Port Name'
$ExcelWorkSheet.Cells.Item(1, 4) = 'Share Name'
# Make the table head bold, set the font size and the column width
$ExcelWorkSheet.Rows.Item(1).Font.Bold = $true
$ExcelWorkSheet.Rows.Item(1).Font.size = 15
$ExcelWorkSheet.Columns.Item(1).ColumnWidth = 28
$ExcelWorkSheet.Columns.Item(2).ColumnWidth = 28
$ExcelWorkSheet.Columns.Item(3).ColumnWidth = 28
$ExcelWorkSheet.Columns.Item(4).ColumnWidth = 28
# Fill in Excel cells with the data obtained from the server
$ExcelWorkSheet.Columns.Item(1).Rows.Item($counter) = $result.Name
$ExcelWorkSheet.Columns.Item(2).Rows.Item($counter) = $result.DriverName
$ExcelWorkSheet.Columns.Item(3).Rows.Item($counter) = $result.PortName
$ExcelWorkSheet.Columns.Item(4).Rows.Item($counter) = $result.ShareName
$counter++
}
# Save the report and close Excel:
$ExcelWorkBook.SaveAs('\User\User\Documents\Working Folder\2132023\test.xlsx')
$ExcelWorkBook.Close($true)
That is because you are cfeating a new Excel COM object inside the loop.
Put that part above the loop, and inside create a new worksheet for each server and fill the data.
Because Get-Printer may very well return more that one object, you need to loop over the results from that too.
Try
# use full absolute path here
$outFile = 'X:\Somewhere\Documents\Working Folder\2132023\test.xlsx'
if (Test-Path -Path $outFile -PathType Leaf) { Remove-Item -Path $outFile -Force }
# Create an Excel object
$ExcelObj = New-Object -comobject Excel.Application
$ExcelObj.Visible = $true
# Add a workbook
$ExcelWorkBook = $ExcelObj.Workbooks.Add()
# Get list of servers from text file
$sites = Get-Content -Path "X:\Somewhere\Documents\Working Folder\2132023\test.txt"
foreach ($site in $sites) {
$counter = 2
# Add a sheet
$ExcelWorkSheet = $ExcelWorkBook.Sheets.Add()
# make this the the active sheet
$ExcelWorkSheet.Activate()
# Rename the worksheet
$ExcelWorkSheet.Name = $site
# Fill in the head of the table
$ExcelWorkSheet.Cells.Item(1, 1) = 'Device Name'
$ExcelWorkSheet.Cells.Item(1, 2) = 'Driver Name'
$ExcelWorkSheet.Cells.Item(1, 3) = 'Port Name'
$ExcelWorkSheet.Cells.Item(1, 4) = 'Share Name'
# Make the table head bold, set the font size and the column width
$ExcelWorkSheet.Rows.Item(1).Font.Bold = $true
$ExcelWorkSheet.Rows.Item(1).Font.size = 15
$ExcelWorkSheet.Columns.Item(1).ColumnWidth = 28
$ExcelWorkSheet.Columns.Item(2).ColumnWidth = 28
$ExcelWorkSheet.Columns.Item(3).ColumnWidth = 28
$ExcelWorkSheet.Columns.Item(4).ColumnWidth = 28
# Fill in Excel cells with the data obtained from the server
Get-Printer -ComputerName $site | Select-Object Name, DriverName, PortName, ShareName | ForEach-Object {
$ExcelWorkSheet.Columns.Item(1).Rows.Item($counter) = $_.Name
$ExcelWorkSheet.Columns.Item(2).Rows.Item($counter) = $_.DriverName
$ExcelWorkSheet.Columns.Item(3).Rows.Item($counter) = $_.PortName
$ExcelWorkSheet.Columns.Item(4).Rows.Item($counter) = $_.ShareName
$counter++
}
}
# Save the report and close Excel:
$ExcelWorkBook.SaveAs($outFile)
$ExcelWorkBook.Close($true)
$ExcelObj.Quit()
# Clean up the used COM objects
$null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($ExcelWorkSheet)
$null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($ExcelWorkBook)
$null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($ExcelObj)
$null = [System.GC]::Collect()
$null = [System.GC]::WaitForPendingFinalizers()
P.S. The code would run faster if you set $ExcelObj.Visible = $false
I'm new to Powershell programming and I have been tasked with exporting email addresses from column 3 in Excel to notepad using Powershell. But the requirement is that I have to start from a specific row. Here's a sample data/script (taken from Steve Fenton - Thanks for the basis Steve!!!!).
If a user inputs Apple (from the read-host prompt), I want to only export the Apple rows' email addresses to notepad (john#email.com and jill#email.com).
Any suggestion/assistance is greatly appreciated!!!! Thank you!!!
Sample Data
{
$sourceFile = "C:\Temp\data.xlsx"
$outFile = "C:\Temp\data.txt"
$stock = Read-Host -Prompt "Which stock?"
$startRow = (depends on input from user)
$col = 3
$usedCellType = 11
$excelApp = New-Object -ComObject Excel.Application
try {
$excelApp.visible = $false;
$excelApp.DisplayAlerts = $false
$workbook = $excelApp.Workbooks.Open($sourceFile)
$worksheet = $workbook.ActiveSheet
$endRow = $worksheet.UsedRange.SpecialCells($usedCellType).Row
$rangeAddress = $worksheet.Cells.Item($startRow, $col).Address() + ":" + $worksheet.Cells.Item($endRow, $col).Address()
Write-Host "Using range $($rangeAddress)"
$worksheet.Range($rangeAddress).Value2 | Out-File -FilePath $outFile
$workbook.Close($false)
}
finally {
$excelApp.Quit()
}
}
So I have a PowerShell Script that takes a CSV file with data inputted from sensors, and converts it into an Excel sheet and then prints it. My problem is that I can not get the format of the Excel sheet to be able to have the width of all my columns fit onto one page. I can have as many portrait pages as I want, but I need all columns to fit on one page.
I have tried: Ranges, Making things bold or not, and using the auto resize feature. None have worked.
$delimiter = ","
$excel = New-Object -ComObject excel.application
$workbook = $excel.Workbooks.Add(1)
$worksheet = $workbook.worksheets.Item(1)
$TxtConnector = ("TEXT;" + $csv)
$Connector = $worksheet.QueryTables.add($TxtConnector,$worksheet.Range("A1"))
$query = $worksheet.QueryTables.item($Connector.name)
$query.TextFileOtherDelimiter = $delimiter
$query.TextFileParseType = 1
$query.TextFileColumnDataTypes = ,1 * $worksheet.Cells.Columns.Count
$query.AdjustColumnWidth = 1
# Execute & delete the import query
$query.Refresh()
$query.Delete()
#Green blocks if number passes
for($i=6;$i -le 84;$i++){
if($worksheet.Cells.Item($i,6).value2 -match "True"){
$Worksheet.Cells($i,4).Interior.ColorIndex = 4
}
if($worksheet.Cells.Item($i,6).value2 -match "False"){
$Worksheet.Cells($i,4).Interior.ColorIndex = 3
}
}
$name1 = $worksheet.Cells.Item(1,6).Text
$name2 = $worksheet.Cells.Item(1,4).Text
# Formatting
$RangeBold = $Worksheet.Range("A1","C92")
$RangeBold.Select()
$RangeBold.Font.Bold=$True
$RangeBold = $Worksheet.Range("E1","E92")
$RangeBold.Select()
$RangeBold.Font.Bold=$True
$RangeDel = $Worksheet.Range("F1","F92")
$RangeDel.Select()
$RangeDel.Delete()
$RangeBorder = $Worksheet.Range("A1","E86")
$RangeBorder.Select()
$RangeBorder.Borders.LineStyle = 1
$RangeBorder.Borders.Weight = 2
# Footer Margin
worksheet("Sheet1").PageSetup.FooterMargin = _
Application.InchesToPoints(0.5)
# Header Margin
Worksheet("Sheet1").PageSetup.HeaderMargin = _
Application.InchesToPoints(0.5)
# Left Margin
Worksheet("Sheet1").PageSetup.LeftMargin = _
Application.InchesToPoints(0.5)
# Right Margin
Worksheet("Sheet1").PageSetup.RightMargin = _
Application.InchesToPoints(0.5)
$xlsx = "C:\ProgramData\CODESYS\CODESYSHMIWinV3\D5050FE1\PlcLogic\data\$name1-$name2.xlsx"
#Printing Excel Sheet
$xlsx.Visible = $true
$workbook = $xlsx.Workbooks.Open('C:\ProgramData\CODESYS\CODESYSHMIWinV3\D5050FE1\PlcLogic\data\$name1-$name2.xlsx')
$worksheet = $workbook.WorkSheets.Item(1)
$xlsx.ActivePrinter = "808_TOSHIBA5508A on WINFPS01.tld-america.com"
$worksheet.PrintOut(1,4,1)
$xlsx.quit()
# Save & close the Workbook as XLSX.
$Workbook.SaveAs($xlsx,51)
$Workbook = $excel.Workbooks.open($xlsx)
$excel.Quit()
Remove-Item $csv
Invoke-Item $xlsx
The sheet still prints with this code, just not formatted how I would like. I need the margins to be smaller, and wanted to center the text in each cell. Any thoughts or other resources to used? I have been google searching for hours and am not finding anything.
$filepath = "C:\Users\Desktop\New folder\Tangent.xlsx"
$sheetname = "sheet"
$objExcel = New-Object -ComObject Excel.Application
$objExcel.Visible = $false
$WorkBook = $objExcel.Workbooks.Open($filepath)
$WorkBook.sheets | Select-Object -Property Name
$WorkSheet = $WorkBook.Sheets.Item($sheetname)
$myObj = [PSCustomObject][ordered]#{
john = $WorkSheet.Range("B1").Text
Rebel = $WorkSheet.Range("B2").Text
MArk = $WorkSheet.Range("B3").Text
Susan = $WorkSheet.Range("B4").Text
Patty = $WorkSheet.Range("B5").Text
}
I have hardcoded all the names into the code which is a weird way of doing it. I want it to read from the Excel directing using command. Can anyone help me please?
Create an empty hashtable and fill it as you iterate over the rows in your Excel sheet, then create the object.
$ht = #{}
$i = 1
while ($WorkSheet.Cells.Item($i, 1).Text) {
$ht[$WorkSheet.Cells.Item($i, 1).Text] = $WorkSheet.Cells.Item($i, 2).Text
$i++
}
$obj = [PSCustomObject]$ht
Untested, as I don't have Excel at hand here.
I have a fairly simple request (for me it is quite tough task tbh).
I have two CSV files which I want to convert to Excel so each of these two CSV files would occupy one sheet each.
So far, I have made it work, but I have this small thing I want to correct.
One of the cells in CSV contains multiple text lines, something like this:
This is entry 1
This is entry 2
I would like to have these two entries to be imported into Excel cell the same way it is in CSV, but when I check my Excel file, the second entry is imported into next row:
Row 1 Cell1 - This is entry 1
Row 2 Cell1 - This is entry 2
I don't know if I should work with .NET class worksheet.UsedRange.EntireRow or worksheet.UsedRange.EntireColumn or something else.
I was checking MSDN, but since I am still a noob, I couldn't find anything.
This is the sample of my code:
Function Merge-CSVFiles
{
Param(
$CSVPath = ".\Reports",
$XLOutput=".\final_final_report.xlsx"
)
$csvFiles = Get-ChildItem ("$CSVPath\*") -Include *.csv
$Excel = New-Object -ComObject excel.application
$Excel.visible = $false
$Excel.sheetsInNewWorkbook = $csvFiles.Count
$workbooks = $excel.Workbooks.Add()
$CSVSheet = 1
Foreach ($CSV in $Csvfiles)
{
$worksheets = $workbooks.worksheets
$CSVFullPath = $CSV.FullName
$SheetName = ($CSV.name -split "\.")[0]
$worksheet = $worksheets.Item($CSVSheet)
$worksheet.Name = $SheetName
$TxtConnector = ("TEXT;" + $CSVFullPath)
$CellRef = $worksheet.Range("A1")
$Connector = $worksheet.QueryTables.add($TxtConnector,$CellRef)
$worksheet.QueryTables.item($Connector.name).TextFileCommaDelimiter = $True
$worksheet.QueryTables.item($Connector.name).TextFileParseType = 1
$worksheet.QueryTables.item($Connector.name).Refresh()
$worksheet.QueryTables.item($Connector.name).delete()
$worksheet.UsedRange.EntireColumn.AutoFit()
$CSVSheet++
}
$workbooks.SaveAs($XLOutput,51)
$workbooks.Saved = $true
$workbooks.Close()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($workbooks) | Out-Null
$excel.Quit()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($excel) | Out-Null
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()
}
I am not sure why the method you are using does that but I was able to reproduce on my system as well. I do some similar operations in one of my scripts that builds a large Excel workbook and I have used the following method with success:
Import the CSV with Import-CSV
Convert it to a multi-dimensional array
Write the array to Value2 of the range object
For example, replace your code that looks like this:
$TxtConnector = ("TEXT;" + $CSVFullPath)
$CellRef = $worksheet.Range("A1")
$Connector = $worksheet.QueryTables.add($TxtConnector,$CellRef)
$worksheet.QueryTables.item($Connector.name).TextFileCommaDelimiter = $True
$worksheet.QueryTables.item($Connector.name).TextFileParseType = 1
$worksheet.QueryTables.item($Connector.name).Refresh()
$worksheet.QueryTables.item($Connector.name).delete()
With this instead:
$CsvContents = Import-Csv $CSVFullPath
$MultiArray = (ConvertTo-MultiArray $CsvContents -Headers).Value
$StartRowNum = 1
$StartColumnNum = 1
$EndRowNum = $CsvContents.Count + 1
$EndColumnNum = ($CsvContents | Get-Member | Where-Object { $_.MemberType -eq 'NoteProperty' }).Count
$Range = $worksheet.Range($worksheet.Cells($StartRowNum, $StartColumnNum), $worksheet.Cells($EndRowNum, $EndColumnNum))
$Range.Value2 = $MultiArray
For that to work you will also need the function I use for converting an object to a multi-dimensional array (based off the one posted here but with some slight modifications):
function ConvertTo-MultiArray
{
param (
$InputObject,
[switch]$Headers = $false
)
begin
{
$Objects = #()
[ref]$Array = [ref]$null
}
process
{
$Objects += $InputObject
}
end
{
$Properties = $Objects[0].PSObject.Properties | ForEach-Object{ $_.Name }
$Array.Value = New-Object 'object[,]' ($Objects.Count + 1), $Properties.Count
$ColumnNumber = 0
if ($Headers)
{
$Properties | ForEach-Object{
$Array.Value[0, $ColumnNumber] = $_.ToString()
$ColumnNumber++
}
$RowNumber = 1
}
else
{
$RowNumber = 0
}
$Objects | ForEach-Object{
$Item = $_
$ColumnNumber = 0
$Properties | ForEach-Object{
if ($Item.($_) -eq $null)
{
$Array.Value[$RowNumber, $ColumnNumber] = ""
}
else
{
$Array.Value[$RowNumber, $ColumnNumber] = $Item.($_).ToString()
}
$ColumnNumber++
}
$RowNumber++
}
$Array
}
}