Add header row to .csv - powershell

I realize this is a very commonly asked question, however none of the solutions that have worked in the questions I have found have worked for me. I have a powershell script that creates a .csv file (using new-item) and then populates it with four columns of information (using add-content). I would like to add to this script to add a header to the .csv so I can further parse it and separate the information by the contents of one of the columns, however nothing I have tried is working. I have tried using the -value parameter to make the first row of the csv the headers I want, which is working to put the header in the correct columns, however the first row of information that is being added through add-content is going into the header row.
Example:
Desired output:
Header | Header | Header | Header
Info | Info | Info | Info
Info | Info | Info | Info
Actual Output:
Header | Header | Header | Header | Info | Info | Info | Info |
Info | Info | Info | Info |
I have also tried using import-csv and adding a header that way, then piping the output to export-csv, however this has been returning a blank .csv each time. Code I have used for that:
Import-Csv $file -header "Header,Header,Header,Header" |export-csv $file -notypeinformation
Way I see it I need to skip the first row when doing add-content, however I'm quite new to powershell and don't know quite how to do that. I've been going in circles on this for a while now, so any input would be much appreciated.
** EDIT **
Answer by TheMadTechnician fixed the issue, pasting currently working code per second half of their answer. It is a relatively simple script to parse a csv, check if there is a tracking number, if there is check against ups website, if delivered deposit in csv, if delivery exception deposit in other csv:
$file= import-csv "**FILEPATH**\Intransit_report.csv"
$newfile= New-Item "**FILEPATH**\$(get-date -format dd-MM-yyyy)_delivered.csv" -type file -force -value "Tag Number,Final Dest,Tracking ID,Attribute Ref #`n"
$exceptionfile= New-Item "**FILEPATH**\$(get-date -format dd-MM-yyyy)_delivery_exceptions.csv" -type file -force -value "Tag Number,Final Dest,Tracking ID,Attribute Ref #`n"
foreach ($i in $file) {
$tagnum= $i | select-object -expandproperty "Tag Number"
$trackingid = $i | select-object -expandproperty "Tracking ID"
$refnum= $i | select-object -expandproperty "Attribute Ref #"
$findest= $i | select-object -expandproperty "Final Dest"
if ($trackingid.length -gt 1){
$uri= "**URI**$trackingid"
$html= Invoke-Webrequest -URI $uri
$fields= $HTML.ParsedHtml
$trackstate= $fields.getElementByID("ttc_tt_spStatus")
if($trackstate.innerText -like "*Business Days*"){
break}
elseif($trackstate.innerText -like "Delivered*"){
"$tagnum, $findest, $trackingid, $refnum" | Add-Content $newfile
}
elseif($trackstate.innerText -like "*Attempt Made*"){
"$tagnum, $findest, $trackingid, $refnum" | Add-Content $exceptionfile
}
}
}

I've got an easy answer, and a good answer. The easy answer is to add `n to your -value parameter.
New-Item C:\Path\To\File.csv -value "Header,Header,Header,Header`n"
That just appends the New Line character to the end of your header text, and anything added after that goes on the next line.
The better answer is that you're probably going about things the hard way. Rather than adding content to a file several times I would recommend collecting the data (unless there is a huge amount), and then outputting it all at once at the end. If you're looking at making a CSV you should make an array of objects, and then just export those objects at the end. Without more data on what you're doing it's hard to give a viable example that you can relate to. Let me know if you want more info on this, and update your question with some more code showing what you're doing.
Edit: Looking at what you have, I think I'd just add a status property to each item, and then export once at the end for each file filtering on the status. Something like this:
$file= import-csv "**FILEPATH**\Intransit_report.csv"
foreach ($i in ($file | Where{$_.'Tracking ID'})) {
$trackingid = $i | select-object -expandproperty "Tracking ID"
$uri= "**URI**$trackingid"
$html= Invoke-Webrequest -URI $uri
$fields= $HTML.ParsedHtml
$trackstate= $fields.getElementByID("ttc_tt_spStatus")
Switch -Regex ($trackstate.innerText){
"Business Days" {Add-Member -InputObject $i -NotePropertyName 'Status' -NotePropertyValue 'In Transit';Continue}
"Delivered" {Add-Member -InputObject $i -NotePropertyName 'Status' -NotePropertyValue 'Delivered';Continue}
"Attempt Made" {Add-Member -InputObject $i -NotePropertyName 'Status' -NotePropertyValue 'Attempt Made'}
}
}
$file | Where{$_.Status -eq 'Delivered'} | Select 'Tag Number','Final Dest','Tracking ID','Attribute Ref #' | Export-CSV "**FILEPATH**\$(get-date -format dd-MM-yyyy)_delivered.csv" -NoTypeInformation
$file | Where{$_.Status -eq 'Attempt Made'} | Select 'Tag Number','Final Dest','Tracking ID','Attribute Ref #' | Export-CSV "**FILEPATH**\$(get-date -format dd-MM-yyyy)_delivery_exceptions.csv" -NoTypeInformation
This way it's doing all of the processing at once, and then writting twice, instead of writing to the drive X number of times, where X is the number of items that are either already delivered or have had delivery attempts made.

Related

Why Powershell outputting this table?

I'm a powershell noob. How come the following code is also outputing the table at the end after the "File to Delete" loop?
$stopwatch = [System.Diagnostics.Stopwatch]::StartNew()
# use partial hashes for files larger than 100KB:
# see documentation at: https://powershell.one/tricks/filesystem/finding-duplicate-files#finding-duplicate-files-fast
$result = Find-PSOneDuplicateFileFast -Path '\\READYNAS\Pictures\2020\10' #-Debug -Verbose
$stopwatch.Stop()
# output duplicates
$allFilesToDelete = #(foreach($key in $result.Keys)
{
#filters out the LAST item in the array of duplicates, because a file name of xxxx (0) comes before one without the (0)
$filesToDelete = $result[$key][0..($result[$key].count - 2)]
#add each remaining duplicate file to table
foreach($file in $filesToDelete)
{
$file |
Add-Member -MemberType NoteProperty -Name Hash -Value $key -PassThru |
Select-Object Hash, Length, FullName
}
}
)
$allFilesToDelete | Format-Table -GroupBy Hash -Property FullName | Out-String | Write-Host
$allFilesToDelete | Sort-Object -Property FullName -OutVariable allFilesToDelete
$allFilesToDelete | Format-Table -Property FullName | Out-String | Write-Host
$confirmation = Read-Host "Are you Sure You Want To Delete $($allFilesToDelete.count) files? (y/n)"
if ($confirmation -eq 'y') {
$i = 0
foreach($fileToDelete in $allFilesToDelete)
{
$i++
Write-Host "$i File to Delete: $($fileToDelete.FullName)"
#Remove-Item $file.FullName -Force -Verbose 4>&1 | % { $x = $_; Write-Host "Deleted file ($i) $x" }
}
} else {
Write-Host "User chose NOT to delete files!"
}
$allFilesToDelete | Sort-Object -Property FullName -OutVariable allFilesToDelete produces output (the input objects in the requested sort order), and since you're not capturing or redirecting it, it prints to the host (display, terminal) by default.
It seems your intent is to sort the objects stored in $allFilesToDelete, which your command does, but it also produces output (the common -OutVariable parameter does not affect a cmdlet's output behavior, it simply also stores the output objects in the given variable); you could simply assign the output back to the original variable, which wouldn't produce any output:
$allFilesToDelete = $allFilesToDelete | Sort-Object -Property FullName
In cases where actively suppressing (discarding) output is needed, $null = ... is the simplest solution:
See this answer for details and alternatives.
Also see this blog post, which you found yourself.
Because the output resulted in implicitly Format-Table-formatted display representations (for custom objects that have no predefined formatting data), the subsequent Read-Host and Write-Host statements - surprisingly - printed first.
The reason is that this implicit use of Format-Table results in asynchronous behavior: output objects are collected for 300 msecs. in an effort to determine suitable column widths, and during that period output to other output streams may print.
The - suboptimal - workaround is to force pipeline output to print synchronously to the host (display), using Out-Host.
See this answer for details.

Powershell script to return search results from a list of keywords

I have a project name called 'SFO104' and I have a list of serial numbers i.e 5011849, 5011850 etc and I have to search a long list of 500+ serial numbers to see if they exist in any other documents not relating to the project name SFO104 or the PO number 114786.
I was thinking of outputting the search results to a csv for each serial number searched but the below isnt working.
$searchWords = gc C:\Users\david.craven\Documents\list.txt
$results = #()
Foreach ($sw in $searchWords)
{
$files = gci -path C:\Users\david.craven\Dropbox\ -filter "*$sw*" -recurse | select FullName
foreach ($file in $files)
{
$object = New-Object System.Object
$object | Add-Member -Type NoteProperty –Name SearchWord –Value $sw
$object | Add-Member -Type NoteProperty –Name FoundFile –Value $file
$results += $object
}
}
$results | Export-Csv C:\Users\david.craven\Documents\results.csv -NoTypeInformation
The image below shows my search of the serial number 5011849 and the results returned correspond to project SFO104 which is as expected.
Your code works, the file is getting populated. However, what you have specified does not have the headers defined as in your screen shot. Also, what does that list.txt look like. My searchlist.txt is a single column file:
Hello
client
Using your code as is, only changing the file path and name, and a slight modification to where the filename is accessed, gives these results...
$searchWords = gc 'D:\Scripts\searchlist.txt'
$results = #()
Foreach ($sw in $searchWords)
{
$files = gci -path d:\temp -filter "*$sw*" -recurse
foreach ($file in $files)
{
$object = New-Object System.Object
$object | Add-Member -Type NoteProperty –Name SearchWord –Value $sw
$object | Add-Member -Type NoteProperty –Name FoundFile –Value $file.FullName
$results += $object
}
}
$results | Export-Csv d:\temp\searchresults.csv -NoTypeInformation
# Results
# psEdit -filenames 'd:\temp\searchresults.csv'
SearchWord FoundFile
---------- ---------
Hello D:\temp\Duplicates\PowerShellOutput.txt
Hello D:\temp\Duplicates\BeforeRename1\PowerShellOutput.txt
Hello D:\temp\Duplicates\PoSH\PowerShellOutput.txt
Hello D:\temp\Duplicates\Text\PowerShellOutput.txt
client D:\temp\Client.txt
client D:\temp\Duplicates\CertLabClients_v1.ps1
client D:\temp\Duplicates\Check Logon Server for Client.ps1
client D:\temp\Duplicates\Create Wireless Hosted Networks in Windows Clients.ps1
...
Update for OP
Since you are using a comma separate list. You need to break that into separate items. I changed my file to this
Hello,client
You cannot match on that layout unless you are trying to match the whole consecutive string. So, if I break the above this way ...
$searchWords = (gc 'D:\Scripts\searchlist.txt') -split ','
… thus the results are as shown before.
Update for the OP
Example, test with this (a different rough approach)...
Foreach ($sw in $searchWords)
{
Get-Childitem -Path "d:\temp" -Recurse -include "*.txt","*.csv" |
Select-String -Pattern "$sw" |
Select Path,LineNumber,#{n='SearchWord';e={$sw}}
}
The LineNumber was sonly added so show where the string was located. Also, note, your code, and what I provide here, will only work for text, csv files.
If you plan to hit these, doc, docx, xls, xlsx, that means way more code as you have to use the default apps Word, Excel, to open and read these files.
This means using the COM Object model for each of those file types in your code. As discussed and shown here:
How do I make powershell search a Word document for wildcards and return the word it found?
You'd need to do a similar thing for Excel or PowerPoint, and if you have PDF, that requires and addon.
Update for OP
Like I said, I put this together quickly so it is a bit rough (no error handling, etc...) by I did test it using my input file and target folder tree and it does work.
# This is what my input looks like
Hello,client
595959, 464646
LIC
Running the code should have given you the results below, using only .txt,.csv files. Using any other file type will error by design as per my comment above regarding, you cannot use this approach for non text-based files without using the native app for the non text file type.
$searchWords = ((gc 'D:\Scripts\searchlist.txt') -split ',').Trim()
Foreach ($sw in $searchWords)
{
Get-Childitem -Path "d:\temp" -Recurse -include "*.txt","*.csv" |
Select-String -Pattern "$sw" |
Select Path,LineNumber,#{n='SearchWord';e={$sw}}
}
Path LineNumber SearchWord
---- ---------- ----------
D:\temp\Duplicates\BeforeRename1\PsGet.txt 157 Hello
...
D:\temp\Duplicates\PoSH\PsGet.txt 157 Hello
...
D:\temp\Duplicates\BeforeRename1\PoSH-Get-Mo... 108 client
D:\temp\Duplicates\BeforeRename1\Powershell ... 12 client
D:\temp\Duplicates\BeforeRename1\Powershell ... 15 client
D:\temp\Duplicates\BeforeRename1\PsGet.txt 454 client
...
D:\temp\newfile.txt 4 client
D:\temp\MyFile.txt 5 595959
D:\temp\ProcessNames.csv 4 595959
D:\temp\Duplicates\Text\JSON-CSS.txt 30 464646
D:\temp\Duplicates\JSON-CSS.txt 30 464646
D:\temp\MyFile.txt 5 464646
D:\temp\ProcessNames.csv 4 464646
D:\temp\Duplicates\BeforeRename1\GetSetScree... 7 LIC

Switch Names from one side to other

I have to set description to a list of sames provided in csv format.
I know I need samaccountnames so i am trying to pull up samaccount from named, unfortunately the names in csv are in reverse order with a header as name
example
Name
Snow, Jon
Starc,arya
lannister,jamie
In a nutshell, I tried
Import-Csv C:\list.csv |
foreach {
$_.Name = "{1}, {0}" -f ($_.Name -split ', ')
$_
No luck, any help is appreciated.
The names should come as -
Jon snow
Arya starc
Jamie lannister
so I can query AD for sam's
To have lastname and firstname you can do this:
$names = $string.split(",")
[array]::Reverse($names)
$names
So if I understood correctly, you want to skip the header (first line). Try changing the following:
Import-Csv C:\list.csv | Select-Object -Skip 1 | ConvertFrom-Csv -Header Name
(or increase '-Skip' amount according to how many lines you want to skip)
The question you have here to me reads a little vague, but I wanted to offer the below. If you specify the header of the CSV when you import it, you can output whatever Property you want, in whatever order:
$test = Import-Csv -Path "C:\temp\test.txt" -Header Last_Name,First_Name
$test | % {"$($_.First_Name) $($_.Last_Name)"}
It was
Import-Csv c:\list.csv |
foreach{
$last,$first = $_.name -split ","
new-object psobject -Property #{name = "$first,$last"}
}

Powershell Searching within a text file and exporting horizontally to CSV

I have a big file which content somme blocks of text data like this.
[SERVER1]
LIBELLE=DATA, SOMME DATA
VARIABLES=A,B,C,D,E
PHYSICAL NAME=E:\SOMME\PATH\FILE.INI
ARTICLE SIZE=50
MACHINE=SOME SERVER
PARAMETER =
OPTION =
[SERVER2]
LIBELLE=DATA2, SOMME DATA2
VARIABLES=A,B,C,D,E
PHYSICAL NAME=Z:\SOMME\PATH\FILE2.INI
ARTICLE SIZE=150
MACHINE=SOME SERVER XY
PARAMETER =
OPTION 1 = VOID
OPTION 2 =
OPTION 3 =
OPTION 4 =
OPTION 5 =
What i would like to do is retrieving every block [SERVERX] and put it into a CSV file in this format (horizontally)
ColumnA__ | ColumnB (LIBELLE)___ | ColumnC (VARIABLES) | ColumnD ect...
[SERVER1] | DATA1, SOMME DATA1 | A,B,C,D,E___________ | Ect...
[SERVER2] | DATA2, SOMME DATA2 | A,B,C,D,E___________ | Ect...
I've tried this, the output work as i want but it need to be automated and exported to scv, which doesn't work for me.
$mydata = Get-Content my_file.txt
write-Host $mydata[0] $mydata[1] $mydata[2] $mydata[3] $mydata[4] $mydata[5] | Export-Csv -Path $rep\results.csv -Force -UseCulture -NoTypeInformation
Tried also somthing with select-string but i dont know if this is the right way to do my job..
select-String -path $my_file -Pattern '\[*\]', 'IDENTIFIANTS=','LIBELLE=','VARIABLES=' | Select-Object -Property LineNumber, Line | Export-Csv -Path $rep\results.csv -Force -UseCulture -NoTypeInformation
Thanks for your advices.
So, I would read the whole file in as a multi-line string using the -Raw switch for Get-Content. Then split the file up based on the [ character to denote records. The get the properties from the ConvertFrom-StringData cmdlet (have to prepend "SERVER=" to each record), and make an object from it. Then we find out what all properties any given record can have, make sure to add them all to the first record if it doesn't have it (this is done because when you export to CSV it bases the columns off of the first entries' property list). Then you can export a CSV.
$Data = (Get-Content my_file.txt -Raw) -split "(\[[^[]+)" | ?{![string]::IsNullOrWhiteSpace($_)}
$Records = $Data -replace '\\','\\'|%{$Record="SERVER="+$_.trim()|ConvertFrom-StringData;New-Object PSObject -Prop $Record}
$Props = $Records|%{$_.psobject.properties.name}|select -Unique
$Props | Where{$_ -notin $Records[0].PSObject.Properties.Name}|%{Add-Member -InputObject $Records[0] -NotepropertyName $_ -NotepropertyValue $Null}
$Records|Export-CSV .\my_file.csv -notype
Edit: For those of you out there running PowerShell 2.0 (3 versions out of date at this point in time), you can't use the -Raw parameter. Here's the alternative:
$Data = (Get-Content my_file.txt) -Join "`r`n" -split "(\[[^[]+)" | ?{![string]::IsNullOrWhiteSpace($_)}
Alternative: Thanks #Matt for the suggestion, it is always good to have a different point of view on these things. As Matt suggested, you can use Out-String to combine the array of strings that Get-Content generates, and end up with a single multi-line string. Here's the usage!
$Data = (GC my_file.txt | Out-String) -split "(\[[^[]+)" | ?{![string]::IsNullOrWhiteSpace($_)}

PowerShell script to export to single CSV

I need help to get the following PowerShell script to output too just one .CSV file instead of the current two. Can someone please help? I'm wanting to get the SNMP settings that are set on remote servers. The information I'm after is held in the registry. $DellAdmKey is one key that holds SubKeys and refers to the "communityNames". Within each of the SubKeys I get the "Value Data" which refers to the "TrapDestinations". $DellAdmKey2 is a Key called "ValidCommunities" but does not have Subkeys, it just has DWORD values that refer to "AcceptedCommunityNames" and the "Rights". So the foreach on the 1st line can't be used as "ValidCommunities" does not contain SubKeys
$servers = "ServerName"
$BaseKey = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey(‘LocalMachine’, $server)
$SubKey= $BaseKey.OpenSubKey("SYSTEM\\CurrentControlSet\\Services\\SNMP\Parameters",$true)
$DellAdmKey = $BaseKey.OpenSubKey("SYSTEM\\CurrentControlSet\\Services\\SNMP\\Parameters\\TrapConfiguration\\",$true)
$DellAdmKey2 = $BaseKey.OpenSubKey("SYSTEM\\CurrentControlSet\\Services\\SNMP\\Parameters\\ValidCommunities\\",$true)
$DellAdmKey.GetSubKeyNames() | foreach {
$action = $_
$subkey = $DellAdmKey.openSubKey($_)
$subkey.GetValueNames() | foreach {$_ | Select
#{name="ServerName";Expression={$server}},
#{Name="CommunityNames";Expression={$action}},
#{name="TrapDestinations";Expression={$subkey.getvalue($_)}}
} | Export-Csv c:\temp\1.csv -NoTypeInformation
}
$action = $_
$subkey = $DellAdmKey2.openSubKey($_)
$subkey.GetValueNames() | foreach {
$_ | Select #{name="ServerName";Expression={$server}},
#{name="AcceptedCommunityNames";Expression={$_}},
#{name="Rights";Expression={$subkey.getvalue($_)}}
} | Export-Csv c:\temp\2.csv -NoTypeInformation
If I understand your question (and your code) correctly, the third group of instructions is supposed to go inside the $DellAdmKey.GetSubKeyNames() | foreach { ... }. In that case you simply need to move the | Export-Csv outside that loop to capture all output in one CSV. You need to make sure all objects have the same set of properties, though.
$DellAdmKey.GetSubKeyNames() | foreach {
$action = $_
$subkey = $DellAdmKey.openSubKey($action)
$subkey.GetValueNames() | Select #{name="ServerName";Expression={$server}},
#{Name="CommunityNames";Expression={$action}},
#{name="TrapDestinations";Expression={$subkey.getvalue($_)}}
#{name="AcceptedCommunityNames";Expression={}},
#{name="Rights";Expression={}}
$subkey = $DellAdmKey2.openSubKey($action)
$subkey.GetValueNames() | Select #{name="ServerName";Expression={$server}},
#{Name="CommunityNames";Expression={}},
#{name="TrapDestinations";Expression={}},
#{name="AcceptedCommunityNames";Expression={$_}},
#{name="Rights";Expression={$subkey.getvalue($_)}}
} | Export-Csv 'c:\temp\out.csv' -NoTypeInformation
Edit: If you really just want to run the code as you posted (which seems somewhat dubious to me, since $_ in line 17 should be empty), you could change the second Export-Csv instruction to
... | Export-Csv 'c:\temp\1.csv' -Append -NoTypeInformation
provided you're running PowerShell v3. Prior to that something like this should do:
... | ConvertTo-Csv -NoTypeInformation | select -Skip 1 |
Out-File 'c:\temp\1.csv' -Append
Either way you should make sure all the objects you're exporting have the same set of properties, though. Otherwise you might end up with Rights values in the TrapDestination column or some such.
I have resorted to using this http://psrr.codeplex.com/ it make life easier. Thanks for the replies Ansgar.