Powershell Text to Column - excel

I have a csv file which contains all data in 1 column.
This is the format,
EPOS SKU QTY ReferenceNr
---- --- --- -----------
717 30735002 1 S04-457312
700 30777125 1 S06-457360
700 25671933 1 S06-457389
716 25672169 1 S09-457296
716 25440683 1 S09-457296
I would like to separate those data into 4 columns with these following headers and save/export to csv or xlsx via powershell script.
Thank you for your help

This should work:
Add-Type -AssemblyName Microsoft.Office.Interop.Excel
$inputFile = $PSScriptRoot + '\rawtext.txt'
$csvFile = $PSScriptRoot + '\rawtext.csv'
$excelFile = $PSScriptRoot + '\rawtext.xlsx'
# Create datatable
$dt = New-Object system.Data.DataTable
[void]$dt.Columns.Add('EPOS',[string]::empty.GetType() )
[void]$dt.Columns.Add('SKU',[string]::empty.GetType() )
[void]$dt.Columns.Add('QTY',[string]::empty.GetType() )
[void]$dt.Columns.Add('ReferenceNr',[string]::empty.GetType() )
# loop file
foreach($line in [System.IO.File]::ReadLines($inputFile))
{
if( $line -match '^\d+' ) {
$contentArray = $line -split ' +'
$newRow = $dt.NewRow()
$newRow.EPOS = $contentArray[0]
$newRow.SKU = $contentArray[1]
$newRow.QTY = $contentArray[2]
$newRow.ReferenceNr = $contentArray[3]
[void]$dt.Rows.Add( $newRow )
}
}
# create csv
$dt | Export-Csv $outputFile -Encoding UTF8 -Delimiter ';' -NoTypeInformation
#create excel
$excel = New-Object -ComObject Excel.Application
$excel.Visible = $false
$excel.DisplayAlerts = $false
[void]$excel.Workbooks.Open($csvFile).SaveAs($excelFile, [Microsoft.Office.Interop.Excel.XlFileFormat]::xlWorkbookDefault)
[void]$excel.Workbooks.Close()
[void]$excel.Quit()
# remove csv
Remove-Item -Path $csvFile -Force | Out-Null

With the Export-Csv instead of Format-Table solved.
$ftr = Get-Content -Path $pathfile |
select -Skip 1 |
ConvertFrom-Csv -Delimiter '|' -Header 'Detail', 'LineNr', 'EPOS', 'SKU',
'SKUName', 'QTY', 'StoreName', 'Contact', 'ReferenceNr' |
Select-Object -Property EPOS, SKU, QTY, ReferenceNr |
Export-Csv -Path $target$ArvName.csv -NoTypeInformation

If your question is regarding Excel... (It is not clear for me)
Just rename the file from *.csv to *.txt and open it on Excel.
On the Text Assistant choose "My data has headers" and "Delimited" and it will be correctly imported with each data on one column. As you ask for.
Later on, save as csv or xlsx.

Related

Clipboard access denied, csv to xlsx conversion

I have a most peculiar issue with clipboard. Below is the code I've written that in essence gathers info about many many thousands of files, compares hashes, compares filnames and lists zero length files and then writes them all to an xlsx file in separate worksheets.
Everything works fine if the scope is relatively small (i.e. ~20k files), but if the scope becomes greater (i.e. ~200k files) I get an error Clipboard access denied. Initially I believed that the issue was clipboard capacity, as I use clipboard and .pastespecial method. But when intermediate csv files are created and their contents copied, everything seems to work fine. Ant thoughts?
Original script
$excel = New-Object -com excel.application
$excel.SheetsInNewWorkbook = 2
$excel.displayalerts = $false
$workbook = $excel.workbooks.add()
$worksheet = $workbook.worksheets
$True_Dups = $worksheet.item(1)
$True_Dups.name = "True Duplicates"
$Dupes = $worksheet.item(2)
$Dupes.name = "Name Duplicates"
$wbPersonalXLSB = $excel.workbooks.open("$env:USERPROFILE\Application Data\Microsoft\Excel\XLSTART\PERSONAL_2.XLSB")
$path = "PATH"
$GCI = GCI $path -file -Recurse -Ea 0
$hashes =
$GCI|
Get-FileHash -Algorithm MD5 -Ea 0|
select Algorithm, Hash, #{l="File";e={$_.Path.split("\")|select -Last 1}},#{l="Path";e={$_.Path.Substring(0,$_.Path.LastIndexof('\'))}}, #{l="Link";e={$_.Path}}|
Group -property "Hash"|
Where {$_.Count -ge 2}|
select -Expand Group
$hashes|ConvertTo-Csv -NoTypeInformation -Delimiter "`t"|scb
$True_Dups.Cells.Item(1).pastespecial()|out-null
$True_Dups.activate()
$excel.run("PERSONAL_2.XLSB!Empty_Row_Dupes")
$filenames =
$GCI|
Select #{l='File';e={$_.PSChildName}}, #{l='Compare Filename';e={$_.BaseName.replace('_','*').replace(' ','*').replace('-','*')}}, Directory, FullName, #{l="Extension";e={$_.Extension}}|
group -Property 'Compare Filename'|
Where {#($_.Group.Extension |Sort -Unique).Count -ge 2}|
select -expand Group
$filenames|ConvertTo-Csv -NoTypeInformation -Delimiter "`t"|scb
$Dupes.Cells.Item(1).pastespecial()|out-null
$Dupes.Activate()
$excel.run("PERSONAL_2.XLSB!Empty_Row_Dupes")
$wbPersonalXLSB.Close()
$Empty = $worksheet.add([System.Reflection.Missing]::Value,$worksheet.Item($worksheet.count))
$Empty.Name = "Zero Lenght"
$zero_length =
$GCI|
? {$_.Length -eq 0}|
Select #{l='File';e={$_.PSChildName}}, Length, Directory, FullName
$zero_length|ConvertTo-Csv -NoTypeInformation -Delimiter "`t"|scb
$zero_length.cells.item(1).pastespecial()|out-null
$zero_length.range("A1:D1").Interior.Color = 8454080
$save = "CSV_PATH"
$workbook.saveas($save)
$workbook.close()
$excel.quit()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($excel)|out-null
Remove-Variable excel
Interdemiate CSVs added
$start = Get-Date
$path = "Path"
$GCI = GCI $path -file -Recurse -Ea 0
$hashes =
$GCI|
Get-FileHash -Algorithm MD5 -Ea 0|
select Algorithm, Hash, #{l="File";e={$_.Path.split("\")|select -Last 1}},#{l="Path";e={$_.Path.Substring(0,$_.Path.LastIndexof('\'))}}, #{l="Link";e={$_.Path}}|
Group -property "Hash"|
Where {$_.Count -ge 2}|
select -Expand Group
$hashes|export-Csv "PATH_1 csv" -NoTypeInformation
$filenames =
$GCI|
Select #{l='File';e={$_.PSChildName}}, #{l='Compare Filename';e={$_.BaseName.replace('_','*').replace(' ','*').replace('-','*')}}, Directory, FullName, #{l="Extension";e={$_.Extension}}|
group -Property 'Compare Filename'|
Where {#($_.Group.Extension |Sort -Unique).Count -ge 2}|
select -expand Group
$filenames|export-Csv "PATH_2.csv" -NoTypeInformation
$zero_length =
$GCI|
? {$_.Length -eq 0}|
Select #{l='File';e={$_.PSChildName}}, Length, Directory, FullName
$zero_length|export-Csv "PATH_3.csv" -NoTypeInformation
$span = ((get-date) - $start).ToString("hh\:mm\:ss")
Write "Span lasted $span"
and conversion script (credit goes to the original creator linked at the end)
$path = "CSV_FOLDER"
$csvs = Get-ChildItem $path -filter *.csv
$y = $csvs.Count
Write-Host "Detected the following CSV files: ($y)"
Write-Host " "$csvs.Name"`n"
$outputfilename = "Final Registry Results"
Write-Host Creating: $outputfilename
$excelapp = New-Object -ComObject Excel.Application
$excelapp.SheetsInNewWorkbook = $csvs.Count
$xlsx = $excelapp.Workbooks.Add()
for($i=1;$i -le $y;$i++) {
$worksheet = $xlsx.Worksheets.Item($i)
$worksheet.Name = $csvs[$i-1].Name
$file = (Import-Csv $csvs[$i-1].FullName)
$file | ConvertTo-Csv -Delimiter "`t" -NoTypeInformation | Clip
$worksheet.Cells.Item(1).PasteSpecial()|out-null
}
$output = "XLSX_OUTPUT"
$xlsx.SaveAs($output)
$excelapp.Quit()
Merging CSV Files into a XLSX with Tabs

How to export a specific Excel column with PowerShell?

I have an excel with multiples columns and I would like to export some specific columns to a .xlsx file but it export the first 3 columns of the excel and not the columns with the specific headers: 'Host','CPU usage %',"Memory usage %"
$SourceFileDirectory = "C:\TEMP\"
$CurrentDate = Get-Date -Format "yyyyMMdd"
$TestFile = "Test2"
$ExcelExt = ".xlsx"
$ExcelFiles = Get-ChildItem $SourceFileDirectory -Filter vHost.xlsx
$headers = 'Host','CPU usage %',"Memory usage %"
foreach ($file in $ExcelFiles)
{
$ImportFile = -JOIN($SourceFileDirectory,$file)
$DestinationFile = -JOIN($SourceFileDirectory,$TestFile,"_",$CurrentDate,$ExcelExt)
$sheetName = 'vHost.xlsx' # => Define the WorkSheet Name here
Write-Host $ImportFile
Write-Host $DestinationFile
$xlsx = Import-Excel -Path $ImportFile -HeaderName $headers -StartRow 1 |
Select-Object * -ExcludeProperty Dupe* |
Export-Excel -Path $DestinationFile -PassThru -WorksheetName $sheetName
$ws = $xlsx.Workbook.Worksheets[$sheetName]
Set-ExcelRange -HorizontalAlignment Center -Worksheet $ws -Range $ws.Dimension.Address
Close-ExcelPackage $xlsx
}
If you know the specific column numbers you can use the -ImportColumns parameter in combination with -HeaderName and the -DataOnly switch if you also want to have new column names.
Minimal example:
$tmp = 'tempfile.xlsx'
#'
col1,col2,col3,col4
1,2,3,4
'# | ConvertFrom-Csv | Export-Excel $tmp
Import-Excel $tmp -ImportColumns 2, 4 -HeaderName NewCol2, NewCol4 -DataOnly
Which yields:
NewCol2 NewCol4
------- -------
2 4

filling a column with a default value for number of rows in file

For the below code id like to add in a default value, lets call it X in the paycentre column in the final output for every row that contains information - can anyone point me in the right direction?
any help greatly appreciated.
$CompletedFile = "C:\Users\filepath.csv"
$path = "C:\Users\originalfile.xls"
$allstaff = #()
#define path for edited docments
Set-Location -Path $path
#Open the Excel file for Whichever csv.
$excelFile = Get-ChildItem -Filter "*file name*"
$excelFile = $path + $excelFile
$excel = new-object -com Excel.Application -Property #{Visible = $false}
#Open the file
$workbook = $excel.Workbooks.Open($excelFile)
#Activate the first worksheet
$sheet = $workbook.Sheets.Item(1)
[void]$sheet.Cells.Item(1, 1).EntireRow.Delete() # Delete the first row
[void]$sheet.Cells.Item(1, 1).EntireRow.Delete() # Delete the 2 row
[void]$sheet.Cells.Item(1, 1).EntireRow.Delete() # Delete the 3 row
[void]$sheet.Cells.Item(1, 1).EntireRow.Delete() # Delete the 4 row
[void]$sheet.Cells.Item(1, 1).EntireRow.Delete() # Delete the 5 row
$workbook.SaveAs($path + "OGEM.csv",6)
#Close workbook and save changes
$workbook.Close($true)
#Quit Excel
$excel.quit()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($excel) | Out-Null
Set-Location -Path $path
$ImportFileNoHeader = Get-ChildItem -Filter "*OGEM*" -Recurse
Clear-Content $CompletedFile
Add-Content $CompletedFile "Title,First Name,Surname,Number,DoB,Amount,EmployeeNo,Location,Paycentre"
#Fill with content from the source file, but miss the last total row
Get-Content $ImportFileNoHeader | Select-Object -SkipLast 2 |Add-Content $CompletedFile
If I understand the question correctly, below code should do what you want.
First off however, you should use the Join-Path cmdlet to joint paths instead of doing the string concatenation like in $path + $excelFile to avoid getting paths that are missing backslash characters in between the parts.
Secondly, Get-ChildItem returns an array of DirectoryInfo and/or FileInfo objects, not simply filenames. To have it return only FileInfo objects (not directories) use the -File switch.
$path = $env:USERPROFILE
$excelOut = Join-Path -Path $path -ChildPath 'OGEM.csv'
$completedFile = Join-Path -Path $path -ChildPath 'Completed-OGEM.csv'
$defaultValue = 'X'
$filter = '*.xlsx'
$excelFile = Get-ChildItem -Path $path -Filter $filter -File | Select-Object -First 1
if ($excelFile) {
$excel = New-Object -ComObject Excel.Application -Property #{Visible = $false}
# Open the file
$workbook = $excel.Workbooks.Open($excelFile.FullName)
# Activate the first worksheet
$sheet = $workbook.Sheets.Item(1)
[void]$sheet.Cells.Item(1, 1).EntireRow.Delete() # Delete the first row
[void]$sheet.Cells.Item(1, 1).EntireRow.Delete() # Delete the 2 row
[void]$sheet.Cells.Item(1, 1).EntireRow.Delete() # Delete the 3 row
[void]$sheet.Cells.Item(1, 1).EntireRow.Delete() # Delete the 4 row
[void]$sheet.Cells.Item(1, 1).EntireRow.Delete() # Delete the 5 row
$workbook.SaveAs($excelOut,6)
# Close workbook and save changes
$workbook.Close($true)
# Quit Excel
$excel.Quit()
# clean-up Com objects
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($sheet) | Out-Null
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($workbook) | Out-Null
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($excel) | Out-Null
$headers = 'Title','First Name','Surname','Number','DoB','Amount','EmployeeNo','Location'
# import the csv file and select all the above headers plus one that is created using a calculated property
$csv = Import-Csv -Path $excelOut -Header $headers -UseCulture | Select-Object *, #{Name = 'Paycentre'; Expression = {$defaultValue}}
Write-Host "Creating completed csv file '$completedFile'"
$csv | Export-Csv -Path $completedFile -UseCulture -Force -NoTypeInformation
}
else {
Write-Warning "Could not find a file using filter '$filter' in path '$path'"
}
I'm using the -UseCulture switch on Import-Csv and Export-Csv in order to match the delimiter character Excel is using on your system
Hope that helps

Passing CSV to Excel Workbook (Not From File)

I have a folder of CSV files that contain log entries. For each entry of the CSV, if the Risk property is not Low and not None then I put it in an accumulation CSV object. From there, I want to import it into an Excel Workbook directly WITHOUT having to save the CSV to file.
$CSVPaths = (Split-Path $PSCommandPath)
$AccumulateExportPath = (Split-Path $PSCommandPath)
$FileName="Accumulate"
$Acc=#()
Foreach ($csv in (Get-ChildItem C:\Scripts\Nessus\Sheets |? {$_.Extension -like ".csv" -and $_.BaseName -notlike "$FileName"}))
{
$Content = Import-CSV $csv.FullName
Foreach ($Log in $Content)
{
If ($Log.Risk -ne "None" -and $Log.Risk -ne "Low")
{
$Acc+=$Log
}
}
}
$CSV = $ACC |ConvertTo-CSV -NoTypeInformation
Add-Type -AssemblyName Microsoft.Office.Interop.Excel
$Script:Excel = New-Object -ComObject Excel.Application
$Excel.Visible=$True
#$Excel.Workbooks.OpenText($CSV) What should replace this?
Is there a Method like OpenText() that lets me pass a CSV object instead of a filepath to a CSV file or am I going to have to write my own conversion function?
Interesting question. I'm not aware of a method that allows you to pass a CSV Object.
However, if your result CSV is not too big and you are using PowerShell 5.0+ you could convert the object to a string and leverage Set-Clipboard (more info)
$headers = ($csv | Get-Member | Where-Object {$_.MemberType -eq "NoteProperty"}).Name
$delim = "`t"
# headers
foreach($header in $headers){
$myString += $header + $delim
}
# trim delimiter at the end, and add new line
$myString = $myString.TrimEnd($delim)
$myString = $myString + "`n"
# loop over each line and repeat
foreach($line in $csv){
foreach($header in $headers){
$myString += $line.$header + $delim
}
$myString = $myString.TrimEnd($delim)
$myString = $myString + "`n"
}
# copy to clipboard
Set-Clipboard $myString
# paste into excel from clipboard
$Excel.Workbooks.Worksheets.Item(1).Paste()
Here is another way to create an Excel spreadsheet from PowerShell without writing a .csv file.
$dirs = 'C:\src\t', 'C:\src\sql'
$records = $()
$records = foreach ($dir in $dirs) {
Get-ChildItem -Path $dir -File '*.txt' -Recurse |
Select-Object #{Expression={$_.FullName}; Label="filename"}
}
#open excel
$excel = New-Object -ComObject excel.application
$excel.visible = $false
#add a default workbook
$workbook = $excel.Workbooks.Add()
#remove worksheet 2 & 3
$workbook.Worksheets.Item(3).Delete()
$workbook.Worksheets.Item(2).Delete()
#give the remaining worksheet a name
$uregwksht = $workbook.Worksheets.Item(1)
$uregwksht.Name = 'File Names'
# Start on row 1
$i = 1
# the .appendix to $record refers to the column header in the csv file
foreach ($record in $records) {
$excel.cells.item($i,1) = $record.filename
$i++
}
#adjusting the column width so all data's properly visible
$usedRange = $uregwksht.UsedRange
$usedRange.EntireColumn.AutoFit() | Out-Null
#saving & closing the file
$outputpath = Join-Path -Path $Env:USERPROFILE -ChildPath "desktop\exceltest.xlsx"
$workbook.SaveAs($outputpath)
$excel.Quit()

Finding and adding data in Excel via Powershell

I have a CSV file that has similar products within it and quantities of each product beside it.
Sample from CSV file
Qty Ordered Product/Item Description Top row (header)
7 Product1
3 Product2
5 Product1
3 Product3
I need a method to find all the similar product#s, add up their Quantities, and place the total of each similar product in a new row.
Add-Type -AssemblyName System.Windows.Forms
$FileBrowser = New-Object System.Windows.Forms.OpenFileDialog -Property
#{
Multiselect = $false # Multiple files can be chosen
Filter = 'Excel (*.csv, *.xlxs)|*.csv;*.xlsx' # Specified file types
}
[void]$FileBrowser.ShowDialog()
$file = $FileBrowser.FileNames;
[Reflection.Assembly]::LoadWithPartialName
("Microsoft.Office.Interop.Excel")|Out-Null
$excel = New-Object Microsoft.Office.Interop.Excel.ApplicationClass
$excel.Visible = $true
$wb = $excel.Workbooks.Open($file)
$ws = $wb.ActiveSheet
$c = $ws.Columns
$c.Item(2).hidden = $true
This code, asks the user to select the csv file, hides useless columns and auto-sizes the important columns as well.
Rather than using Excel as a COM Object you could use Import-CSV and then Group-Object. Then loop through the groups for the information you need.
Add-Type -AssemblyName System.Windows.Forms
$FileBrowser = New-Object System.Windows.Forms.OpenFileDialog -Property #{
Multiselect = $false # Multiple files can be chosen
Filter = 'Excel (.csv, *.xlxs)|.csv;*.xlsx' # Specified file types
}
[void]$FileBrowser.ShowDialog()
ForEach ($file in $FileBrowser.FileNames) {
$CSV = Import-CSV $file | Add-Member -Name Total -Value 0 -MemberType NoteProperty
$Groups = $CSV | Group-Object "Product/Item Description"
$NewCSV = Foreach ($Group in $Groups) {
$Count = 0
$Group.Group."Qty Ordered" | ForEach-Object {$Count += $_}
Foreach ($value in $CSV) {
If ($value."Product/Item Description" -eq $Group.Name) {
$value.Total = $Count
$value
}
}
}
Export-CSV "$filenew" -NoTypeInformation
}

Resources