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.
Related
I need to import a CSV file and then replace full usernames domain\username with username.
The following lines work but I only receive the amended usernames as the output and not the full file.
Could you please advise?
$TestFile = Import-Csv .\file.csv
$NewFile = $TestFile | ForEach-Object {$_."Username" -replace 'domain\\',''}
When processing CSV input with a ForEach-Object loop you need to output the data back to the pipeline. Also, the -replace operator doesn't modify variables or properties in-place. It takes the value, does the work, and outputs the modified string to the success output stream. If you want to update a property you need to assign the modified value back to that property.
Change this:
$TestFile = Import-Csv .\file.csv
$NewFile = $TestFile | ForEach-Object {$_."Username" -replace 'domain\\',''}
into this:
$NewFile = Import-Csv .\file.csv | ForEach-Object {
$_.Username = $_.Username -replace 'domain\\', '' # update username
$_ # feed data back into the pipeline
}
and the code will do what you want.
You can perform the replace on the string data, then convert it into an object using ConvertFrom-Csv.
$TestFile = (Get-Content .\file.csv) -replace 'domain\\',''
$NewFile = ConvertFrom-Csv $TestFile
Here's one way - get the column names from the input table, iterate each row in the table, and output new custom objects with needed changes.
$table = Import-Csv "Test.csv"
# Get column names
$columnNames = ($table | Select-Object -First 1).PSObject.Properties |
Select-Object -ExpandProperty Name
# Iterate each row in the table
foreach ( $row in $table ) {
$outputObject = New-Object PSObject
foreach ( $columnName in $columnNames ) {
if ( $columnName -eq "Username" ) {
$outputObject | Add-Member NoteProperty "Username" ($row.Username.Split('\')[1])
}
else {
$outputObject | Add-Member NoteProperty $columnName $row.$columnName
}
}
$outputObject
}
To create a new CSV file as output, put the above code in a script and pipe to Export-Csv.
I am trying to compare 2 csv files using 'user Id' column which is common in both the files and want to append the missing entries in csv 1 from csv 2 using power shell
If the first CSV file has missing rows, and they need to be copied in from the second CSV file directly:
ipcsv 2.csv |? 'User Id' -notin (ipcsv 1.csv).'User Id' | epcsv 1.csv -Append
If you're also saying that User IDs are unique in your files, you could instead take the unique rows out of the set of both CSVs into the first CSV:
(ipcsv 1.csv, 2.csv) | sort -Unique 'User Id' | epcsv 1.csv -NoTypeInformation
Alternatively, if you're saying that the files have the same User Ids but don't have the same other columns, and the entries in the first CSV are present but have some empty columns, then:
$csv1 = Import-Csv d:\file1.csv
$csv2 = Import-Csv d:\file2.csv
foreach ($csv1item in $csv1)
{
$csv2item = $csv2.Where{$_.'User Id' -eq $csv1item.'User Id'}
$item1Properties = $csv1item | Get-Member -MemberType NoteProperty | select -ExpandProperty Name
$item2Properties = $csv2item | Get-Member -MemberType NoteProperty | select -ExpandProperty Name
$sharedProperties = $item1Properties.where{$_ -in $item2Properties}
$sharedProperties | ForEach-Object {
$value = $csv1item."$_"
if ($value -eq '') {
$csv1item."$_" = $csv2item."$_"
}
}
}
$csv1 | Export-Csv D:\file1.csv -NoTypeInformation
Alternatively again, if you're saying the two files have different columns which overlap at 'User Id' and also the entries are missing from the first CSV entirely, rather than being present with missing columns, then this will add them from the second CSV, taking only the properties which overlap between them (hopefully):
$csv1 = Import-Csv d:\file1.csv
$csv2 = Import-Csv d:\file2.csv
$item1Properties = ($csv1[0] | gm -M NoteProperty).Name
$newCsv1Items = foreach($csv2item in $csv2.Where{$csv1.'User Id' -notcontains $_.'User ID'}) {
$newcsv1Item = #{}
$item1Properties.ForEach{$newcsv1Item."$_" = $csv2item."$_"}
[PSCustomObject]$newcsv1Item
}
$newCsv1Items | Export-Csv -Append D:\file1.csv
I'm looking for a way to add an extra member to an object in PowerShell without creating a new object and looping.
Typically when I run a script it will be again a list of servers and I want to append some extra information against the data I'm returning but I can't find a way to do it without creating a new PSObject and looping through the existing object and adding the extra member row by row (simplified example below)
$FileList = get-childitem | Select-Object Name, DirectoryName
$FileListOBJ = #()
foreach ($item in $FileList)
{
$Temp = New-Object PSObject
$Temp | Add-Member NoteProperty ServerName "XServerName"
$Temp | Add-Member NoteProperty FileName $item.Name
$Temp | Add-Member NoteProperty Directory $item.DirectoryName
$FileListOBJ += $Temp
}
$FileListOBJ
Is there a way to do it along these lines ...
get-childitem | Select-Object "ServerName", Name, DirectoryName
The above code creates the extra Member but I haven't been able to find a way to fill the additional member with the details I'm after.
you can also create a new object without the add-member calls:
$FileList = get-childitem | Select-Object Name, DirectoryName
$FileListOBJ = #()
foreach ($item in $FileList)
{
$FileListOBJ += [PSCustomObject]#{ServerName="ServerName";FileName=$item.Name;Directory=$item.DirectoryName}
}
$FileListOBJ
Sure, you can use a calculated property (more about them here):
get-childitem | Select-Object #{l="ServerName"; e={"XServerName"}}, Name, DirectoryName
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".
I have this code that I am running from powershell. When I run it without the export-csv i get all the folder names on the screen.
dir | select -expand fullname | % { ($_ -split '\')[7]
But if I add | export-csv c:\test.txt then I see following in the file not the folder name I expected just like I see it on the screen.
#TYPE System.String
"Length"
"13"
"18"
"20"
"22"
"29"
"21"
"24"
"11"
"17"
"20"
"20"
Export-Csv exports a table of object properties and their values. Since your script is producing string objects, and the only property they have is length, that's what you got.
If you just want to save the list, use Out-File or Set-Content instead of Export-Csv.
The previous answer does work, but what if someone was looking to output it into a CSV file.
This does NOT work:
$str_list = #('Mark','Henry','John')
$str_list | Export-Csv .\ExportStrList.csv -NoType
Because Export-Csv takes Objects and outputs properties. The only properties for a String[ ] is Length, so the CSV file only contains Lengths.
To fix this we need to change the String[ ] into an Object[ ]. The simplest way is with Select-Object.
Put each String into the Name property of a new Object[ ], like this:
$str_list = #('Mark','Henry','John')
$obj_list = $str_list | Select-Object #{Name='Name';Expression={$_}}
$obj_list | Export-Csv .\ExportStrList.csv -NoType
Just to re-iterate, Select-Object outputs a custom PSObject that can easily be manipulated. This is very powerful information, use it wisely.
This worked for me:
$data = #()
$row = New-Object PSObject
$row | Add-Member -MemberType NoteProperty -Name "name1" -Value "Test"
$row | Add-Member -MemberType NoteProperty -Name "name2" -Value 2
$data += $row
$data | Export-Csv "Text.csv" -NoTypeInformation
This is another way to handle this issue:
Out-File outputs by default
Define the master array list
$MASTER_ARRAY_LIST = [System.Collections.ArrayList]#()
Define the output filename
$OutputFilename="C:\TEMP\MyOutputFile.csv"
ForEach ( $Something in $List_of_Somethings) {
$CURRENT_RECORD_DETAILS = New-Object PSObject -Property #{'name'=$($Something.Name);'fullname'=$($Something.FullName);'id'=$($Something.ID)}
$MASTER_ARRAY_LIST.Add( $CURRENT_RECORD_DETAILS ) > $null
}
$MASTER_ARRAY_LIST.ToArray() | Select-Object -Property name,fullname,id | Export-Csv -Path $OutputFilename -NoTypeInformation
$test = #("test1","test2","test3")
$test | export-csv "firstTry.csv"
#We see that this fails. So here is how to to do it with the desired results
foreach ($item in $test) {
[ pscustomobject]#{ ResultColumn = $item } | Export-Csv -Path ./secondTry.csv -NoTypeInformation -Append
}
$output |Select-Object * | Export-Csv 'h:\filename.csv' -NoTypeInformation