powershell convert array of hastables into csv - powershell

Have done googling but have not found suitable answer
I have
$a = #(
#{"name"="a"; "value"="1"},
#{"name"="b"; "value"="2"}
)
Foreach($x in $a){
$obj = New-object psobject
Foreach($key in $x.keys) {
$obj ¦ add-member -membertype NoteProperty -name $key -value $x[$key]
}
$obj ¦ export-cvs test.csv
}
I get only last line in cab. I want all the lines

It's truth that Export-Csv won't append (< v3), but I do not think ConvertTo-Csv will solve the problem... In the end you will get file with headers repeated for every object in your array. I would suggest one of two things:
move Export-Csv outside foreach {} (need to put it in subexpression though, because it won't be possible to pipe it by default)
use Foreach-Object instead and get same results
I would also suggest to use New-Object PSObject -Property parameter instead of adding members later: because you use hashtable as a base you are half way there, see attached code samples:
# Using $( foreach ($x in $a) {} ) | Export-Csv
$(Foreach($x in $a){
New-object psobject -Property $x
}) | Export-Csv test.csv
# Using $a | Foreach-Object {} | Export-Csv
$a | ForEach-Object {
New-Object PSObject -Property $_
} | Export-Csv test.csv

You're overwriting the csv file each time. Try using convertTo-CSV each time (instead of export-csv) and appending that to a file.

2022 update
You always need to use the -append parameter if you are iterating through a collection
using powershell 7.2 and later you don`t need to parse the hashtable again, it matches the repeating properties and writes to csv.
so this will work automatically
$hashtable=#()
$hashtable += $pscustomobject
$hashtable | export-csv

Related

Add column CSV in existing CSV with powershell

I have a Powershell script, which collects the size of a backup, and exports it to CSV, I would like to know if it is possible that it could be added to the next csv column, or an excel.
I've been looking at the documentation, because I think it looks better on an excel, but I can't add one more column, I always believe it from scratch.
$today = (get-date).Date
$backup = Get-VBRBackup | where {$_.info.jobname -eq "A. ProduccionInterna.Infraestructura Backup Copy"}
if ($backup) {
$backup.GetAllStorages() | where {$_.CreationTime.Date -eq $today} | select {$_.PartialPath}, {$_.Stats.BackupSize/1GB} |
export-csv -Path C:\Users\acepero\Documents\test.csv -NoTypeInformation -Delimiter ';'
}
UPDATE
I have managed to create new columns once, then it gives an error:
Select-Object : The property cannot be processed because the property "{$_.PartialPath}, {$_.Stats.BackupSize/1GB} , {$Session.BackupStats.DedupRatio} ,
{$Session.BackupStats.CompressRatio}" already exists.
The code now has this form
$today = (get-date).Date
$backup = Get-VBRBackup | where {$_.info.jobname -eq "A. ProduccionInterna.Infraestructura Backup Copy"}
if ($backup) {
$backup.GetAllStorages() | where {$_.CreationTime.Date -eq $today} | select {$_.PartialPath}, {$_.Stats.BackupSize/1GB} , {$Session.BackupStats.DedupRatio} , {$Session.BackupStats.CompressRatio}
(Import-Csv "C:\Users\acepero\Documents\test.csv") |
Select-Object *, {{$_.PartialPath}, {$_.Stats.BackupSize/1GB} , {$Session.BackupStats.DedupRatio} , {$Session.BackupStats.CompressRatio}} |
Export-csv -Path C:\Users\acepero\Documents\test.csv -NoTypeInformation #-Delimiter ';'
}
When you take output from a command and pipe it through select, you are creating an output object, which has the selected values as properties. Here is an example using the Get-ChildItem command:
$result = Get-ChildItem C:\Temp | select Name, Length
The $result array contains objects which have the "Length" and "Name" NoteProperties. When you pipe that object to Export-CSV, it creates one column for each Property/NoteProperty the object has. In order to 'add a column to the CSV', all you need to do is add a NoteProperty to the object. You can do that with the Add-Member cmdlet, like this:
$result | Add-Member -MemberType NoteProperty -Name 'ColumnName' -Value 'ColumnValue'
Be careful how you do this. If $result is a single object, this command will add the NoteProperty/Value pair to that object. If $result is an array of objects, it will add that NoteProperty/Value pair to all objects held in the array. If you need to assign different values to each object, you'll need to iterate through the array:
ForEach ($res in $result)
{
$thisvalue = '' #Assign specific value here
$res | Add-Member -MemberType NoteProperty -Name 'ColumnName' -Value $thisvalue
}
I hope this helps you. If it does, please don't forget to accept the answer.

Reformat CSV file in PowerShell

I have a well-formed CSV file that I want to read, reformatting some values while doing so. For some reason the code below does not work ($csv variable ends up being empty):
$csv = Import-Csv "result.log" -Delimiter "|" | ForEach-Object {
$_.PSObject.Properties.Value | ForEach-Object {
if ($_ -like '*---*') { $_ = "" }
}
}
What am I doing wrong?
Your result is blank because you aren't returning anything to the pipeline (you are just changing the value of one or more properties but then not outputting them).
There might be a simpler solution but I think this achieves what you want:
$CSV = Import-Csv "result.log" -Delimiter "|" | Foreach-Object {
$Output = New-Object -TypeName PSObject
$_.PSObject.Properties | Foreach-Object {
If ($_.Value -like '*---*') { $_.Value = '' }
$Output | Add-Member -Name $_.Name -Value $_.Value -MemberType NoteProperty
}
$Output
}
This performs the following for each row of the CSV:
creates a new empty PowerShell object
loops through the properties of the CSV, blanking ones that include --- and then uses Add-Member to add those properties to our object
outputs the new object to the standard pipeline
The result of this goes to the $CSV variable which you then could output as CSV via Export-CSV (or you could skip putting it in the variable and use Export-CSV on the end of the outer ForEach-Object).

try to determine the max number of character in each column

I have written a script that tries to determine the max no. of character for each column. This is what I wrote:
$path = 'folder path'
$file = Get-ChildItem $path\*
$FileContent = foreach ($files in $file) {
$FileHeader = #( (Get-Content $files -First 1).Split($delimiter) )
$importcsv = #( Import-Csv $files -Delimiter "$delimiter" )
for ($i=0; $i -lt $FileHeader.Length; $i++) {
#select each column
$Column = #( $importcsv | select $FileHeader[$i] )
#find the max no. of character
$MaxChar = #(($Column[$i] |
Select -ExpandProperty $FileHeader[$i] |
Measure-Object -Maximum -Property Length).Maximum)
$output = New-Object PSObject
$output | Add-Member NoteProperty FullName ($files.FullName)
$output | Add-Member NoteProperty FileName ($files.Name)
$output | Add-Member NoteProperty Time (Get-Date -Format s)
$output | Add-Member NoteProperty FileHeader ($($FileHeader[$i]))
$output | Add-Member NoteProperty MaxCharacter ($($MaxChar[$i]))
Write-Output $output
}
}
The script above is just part of it, so $delimiter is already defined. And finally I will export the result as CSV.
The script runs without any error, but when I open the file it only gives me the first column/header the max no. of character, and the rest of column/header are missing.
The perfect result will be showing each column/header the max no. of character.
Is something wrong with my loop?
my boss is trying to create an automate process to finding all the information from the raw data and use those information to upload to the database, so part of the script that is missing is about determine the delimiter of the raw file, the $CleanHeader is clean version of $FileHeader (remove all special characters, turn capital letters to small letters), those cleanheaders will be use for headers in the table in the database. and he also want to know the maximum character in each column, so that info can use them in creating the size of the column in the table in the database (he knows this part can be done in sql), but he ask me whether it can be done in PowerShell or not.
This should work:
$ht = #{}
# import a CSV and iterate over its rows
Import-Csv $f.FullName -Delimiter "$delimiter" | ForEach-Object {
# iterate over the columns of each row
$_.PSObject.Properties | ForEach-Object {
# update the hashtable if the length of the current column value is greater
# than the value in the hashtable
if ($_.Value.Length -gt $ht[$_.Name]) {
$ht[$_.Name] = $_.Value.Length
}
}
}
# create an object for each key in the hashtable
$date = Get-Date -Format s
$ht.Keys | ForEach-Object {
New-Object -Type PSObject -Property #{
FullName = $f.FullName
Name = $f.Name
Time = $date
FileHeader = $_
MaxCharacter = $ht[$_]
}
}
FileHeader[$i] was returning the column name with quotes : "ColumnName" instead of ColumnName
To fix, just add a trim to the line where you pull the header :
$FileHeader = #( (Get-Content $files -First 1).Split($delimiter).trim('"') )

Powershell. Need help importing hostnames from .csv. The rest of this works

I am trying to figure out how to correct this script I've wrote. I know it is something wrong with the way it is importing the list of hostnames. I don't know how to fix it.
Part 1: This is supposed to import a .csv with the hostnames and dig the registry for the application's uninstall information, put it into an array, and export into .csv's for later use. Also it creates .txt files in order to later compare the applications on the system to a baseline.
$path = "\\path"
$computers = Import-Csv -Path "\\Path\hostnames.csv"
$array = #()
foreach($pc in $computers)
{
$computername = $pc.computername
#$computername = "KNOWN_HOSTNAME" #test line for one system
$UninstallKey = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall"
$reg = [microsoft.win32.registrykey]::OpenRemoteBaseKey('LocalMachine',$computername)
$regkey = $reg.OpenSubKey($UninstallKey)
$subkeys = $regkey.GetSubKeyNames()
foreach($key in $subkeys)
{
$thisKey=$UninstallKey+"\\"+$key
$thisSubKey=$reg.OpenSubKey($thisKey)
$obj = New-Object PSObject
$obj | Add-Member -MemberType NoteProperty -Name "ComputerName" -Value $computername
$obj | Add-Member -MemberType NoteProperty -Name "DisplayName" -Value $($thisSubKey.GetValue("DisplayName"))
$obj | Add-Member -MemberType NoteProperty -Name "DisplayVersion" -Value $($thisSubKey.GetValue("DisplayVersion"))
$obj | Add-Member -MemberType NoteProperty -Name "Publisher" -Value $($thisSubKey.GetValue("Publisher"))
$obj | Add-Member -MemberType NoteProperty -Name "InstallDate" -Value $($thisSubKey.GetValue("InstallDate"))
$array += $obj
$ExportArray = $array | Where-Object { $_.DisplayName } |
select ComputerName , DisplayName, DisplayVersion, Publisher, InstallDate
$ExportArray |
Export-csv $path\$computername.csv -NoTypeInformation
$ExportArray2 = $array |
Where-Object { $_.DisplayName } |
select DisplayName, DisplayVersion, Publisher
$ExportArray2 |
Export-csv $path\$computername.txt -NoTypeInformation
}
}
Part 2: This portion compiles the .csv's into one excel document for reporting
$csvs = Get-ChildItem $path\* -Include *.csv
$outputfilename = "Network_" + (Get-Date -Format yyyyMMdd)
$excelapp = new-object -comobject Excel.Application
$excelapp.sheetsInNewWorkbook = $csvs.Count
$xlsx = $excelapp.Workbooks.Add()
$sheet=1
foreach ($csv in $csvs)
{
$row=1
$column=1
$worksheet = $xlsx.Worksheets.Item($sheet)
$worksheet.Name = $csv.Name
$file = (Get-Content $csv.PSPath | ForEach-Object {$_ -replace '"', ""})
foreach($line in $file)
{
$linecontents = $line -split ‘,(?!\s*\w+”)’
foreach($cell in $linecontents)
{
$worksheet.Cells.Item($row,$column) = $cell
$column++
}
$column = 1
$row++
}
$sheet++
}
$output = $path + “\” + $outputfilename + ".xlsx"
$xlsx.SaveAs($output)
$excelapp.quit()
Part 3: This portion loads up a baseline, and the .txt's created preciously, and checks for differences in the files. (also deletes blank ouput files)
$bline = Get-ChildItem $path\* -Include Baseline.txt
$txts = Get-ChildItem $path\* -Include *.txt -Exclude Baseline.txt
foreach ($txt in $txts)
{
Compare-Object -referenceobject $(Get-Content $bline) -differenceobject $(Get-Content $txt) |
ft inputobject, #{n = "file"; e = {if ($_.SideIndicator -eq '=>') {"System"} else {"Baseline"}}} |
Out-File $txt'_has_diff'.csv -Width 256
Get-ChildItem $path |
where {$_.Length -eq 0} |
Remove-Item
}
Thank you
Edit:
The Hostnames.csv files I've tried are:
HOSTNAME1
HOSTNAME2
and
"HOSTNAME1","HOSTNAME2"
It's a little unclear what the problem is, because you say there is "something wrong with the way it is importing the list of hostnames", but you haven't specified what kind of results you're getting and how they differ from the intended results.
However, based on your sample data I think I can infer what the problem is: You're trying to use Import-Csv on non-CSV data. Neither of your examples looks like a CSV file. They both look like lists. A list in which the items are separated by commas, such as
"HOSTNAME1","HOSTNAME2","HOSTNAME3","HOSTNAME4"
is not called a "CSV file". CSV files are a form of "flat file", in which the data represents the rows and columns of a single database table. An example of a CSV file would be something like this, where the first line is a list of field (column) names, and the other lines are records (rows) with the comma-separated values corresponding to the columns in the header row:
"Hostname","OS","OS Version","Primary Function","Location"
"BOSEXCH01","Windows","Server 2012","Microsoft Exchange","Boston"
"BOSDC01","Windows","Server 2008 R2","Active Directory domain controller","Boston"
"MYWEB","Linux","Ubuntu 13.04","Apache web server","Phoenix"
The cmdlet Import-Csv imports a CSV file into an array of objects in which the properties are the field names in the header row, and the values are the comma-separated items in each row corresponding to the property names derived from the header row. Export-Csv does the reverse—it creates a CSV file from an array of objects.
It looks like what you're trying to do is read a simple list of hostnames into an array of strings. If your data looks like the first example,
HOSTNAME1
HOSTNAME2
[etc...]
you can read it into an array by simply using Get-Content, as follows (note that I changed the extension to .txt to reflect the actual format of the data):
$computers = Get-Content "\\Path\hostnames.txt"
If your data looks like the second example,
"HOSTNAME1","HOSTNAME2",[etc...]
you can read it into array like this:
$computers = (Get-Content "\\Path\hostnames.txt") -split ','
On the other hand, it appears that you are using Export-Csv correctly: You're exporting a bunch of objects with the same properties into a flat file, which is the correct usage of the term "CSV".

Powershell Append CSV fail

I am fairly new to powershell, but when I try to run this portion of the code, it works sometimes, but I usually get an error message:
"Method invocation failed because [System.Management.Automation.PSObject] doesn't contain a method named 'op_Addition'."
It seems to fail when I add $in + $o, and it has worked in the past. I haven't changed anything for it to stop working though. I am trying to append to a csv, and I have tried Export-CSV -append, however that does not work for me. Any ideas on what might be wrong with this code?
$in = Import-Csv $csv
$obj = New-Object PSObject
$obj | Add-Member NoteProperty Column1 "$c1"
$obj | Add-Member NoteProperty Column2 "$c2"
$obj | Add-Member NoteProperty Column3 "$c3"
$in + $obj | Export-Csv -Encoding ASCII -NoTypeInformation $csv
That probably worked before when $in is an array. However if you read in a CSV with just a single element it will fail. You can do this instead in PowerShell 3.0 (you don't need to even read in $in):
$obj | Export-Csv $csv -Encoding ASCII -NoTypeInformation -Append
The -Append parameter on Export-Csv is new in PowerShell 3.0. If you don't have 3.0 then modify this line:
$in = #(Import-Csv $csv)
Then it should work. That makes sure that $in is always an array.