Powershell loop csv file export csv line one at a time - powershell

I'm trying to loop a pipe delimited file, check if the line has 28 columns and just export that line to the file. This is the primary question and answer I'm looking for (I have researched a lot, but I'm new to PS and so many different ways I need some help). The following works but there are two issues. The export file will not let me choose pipe delimited, AND the output goes in alphabetical format by field name, not by ordinal.
Also, is there a way I can make the output not "text" qualified?
$path = Get-ChildItem c:\temp\*.txt
$staticPath = '0859'
$year = Get-Date -format yy
$month = Get-Date -format MM
$day = Get-Date -format dd
$output = 'c:\temp\' + $year + $month + $day + $staticPath + '.TXT'
$outputbad = 'c:\temp\BAD' + $year + $month + $day + $staticPath + '.TXT'
$input = 'c:\temp\' + $path.Name
$input
$csv = Import-Csv -path $input -Delimiter "|"
foreach($line in $csv)
{
$properties = $line | Get-Member -MemberType Properties
$row = ''
$properties.Count
$obj = new-object PSObject
for($i=0; $i -lt $properties.Count;$i++)
{
$column = $properties[$i]
$columnvalue = $line | Select -ExpandProperty $column.Name
#$row += $columnvalue
$obj | add-member -membertype NoteProperty -name $column.Name -value $columnvalue
}
if($properties.Count -eq 28)
{
$obj | export-csv -Path $output -Append -NoTypeInformation
#$row | Export-Csv -Path $output -Append -Delimiter "|" -NoTypeInformation
}
else
{
$obj | Export-Csv -Path $outputbad -Append -Delimiter "|" -NoTypeInformation
}
}

If you want to avoid any chance of changing the formatting of these lines files, perhaps you don't want to use the -CSV commands. Export-csv can add quotation marks etc. Here's different way that might do what you want:
$path | ForEach-Object {
$good = #()
$bad = #()
Get-Content $_ | ForEach-Object {
if (($value = $_ -split '\|').length -eq 28) {
$good += $_
} else {
$bad += $_
}
}
if ($good) { Out-File -Append -InputObject $good $output }
if ($bad) { Out-File -Append -InputObject $bad $outputbad }
}
Note however that this will count quoted values containing a pipe differently than import-csv. Pipe separated values are sometimes generated without any quoting logic.
The $values variable will be an array of individual columns, so if you want to write some code to fix them up inside the if, you can use that then join them back up with $good += $values -join '|', or perhaps use another regex to fix errors.

silly me, here is the answer. But if somebody could answer why the PSObject or the properties loop is alphabetical it would be helpful. Soon i would like to add intelligence to this to check ordinal (not alphabetic field name order) a field if its a integer or not, then i know how to fix the BAD record.
$path = Get-ChildItem c:\temp\*.txt
$staticPath = '0859'
$year = Get-Date -format yy
$month = Get-Date -format MM
$day = Get-Date -format dd
$output = 'c:\temp\' + $year + $month + $day + $staticPath + '.TXT'
$outputbad = 'c:\temp\BAD' + $year + $month + $day + $staticPath + '.TXT'
$input = 'c:\temp\' + $path.Name
$csv = Import-Csv -path $input -Delimiter "|"
foreach($line in $csv)
{
$properties = $line | Get-Member -MemberType Properties
if($properties.Count -eq 28)
{
$line | export-csv -Path $output -Append -NoTypeInformation -Delimiter "|"
}
else
{
$line | Export-Csv -Path $outputbad -Append -Delimiter "|"
}
}

Related

Import CSV with loop problem - The result contains only last item from the CSV

I need to import a few groups from a CSV file and then export members - but I need it like one row each, something like:
"Group_name1", "member1, member2,member3"
"Group_name2", "member1,member2,member3"
"Group_name3", "member1,member2,member3"
And my script is working fine for a single group but I'm having troubles with adding a for-each loop - the result contains only last item from the CSV..
#$DL='plum'
$DL_List = "C:\ps1\shared_mailboxes\groups.csv"
$DL_array = (Import-Csv -Path $DL_List).name
foreach ($DL in $DL_array)
{
$DL_Membership = (Get-DistributionGroupMember -identity $DL).displayName
if([string]$DL_Membership -ne "")
{
$Members = ""
foreach($DL_Membership in $DL_Membership)
{
if($Members -ne "")
{
$Members=$Members +","
}
$Members = $Members + $DL_Membership
}
}
}
$ExportCSV=".\group_members_$((Get-Date -format yyyy-MMM-dd-ddd` hh-mm` tt).ToString()).csv"
$Result = #{'Group'=$DL;'Users'=$Members}
$Results = New-Object PSObject -Property $Result
$Results |select-object 'Group','Users' | Export-Csv -Path $ExportCSV -Notype -Append
I googled it but I'm not sure what I should change in my script..
The script has been re-writed using -join operator and now it's working as excepted.
$DL_List = Import-Csv "C:\ps1\shared_mailboxes\groups.csv"
$CsvOut = foreach ( $dl in $DL_List ) {
$Group = $dl.name
$MemberArray = (Get-DistributionGroupMember -identity $Group).displayName
$MemberList = $MemberArray -join ','
# generate the output object
# that goes in $CsvOut
[PSCustomObject] #{
Group = $Group
Members = $MemberList
}
}
$Timestamp = Get-Date -Format 'yyyy-MM-dd-HHmm' # this is a string
$CsvOutPath = "c:\ps1\shared_mailboxes\memberlist.$Timestamp.csv"
$CsvOut | Export-Csv $CsvOutPath -NoTypeInfo
I think you are over-complicating this.
If your desired result is a CSV file, then output objects straight away like below:
$DL_List = 'C:\ps1\shared_mailboxes\groups.csv'
$DL_array = (Import-Csv -Path $DL_List).name
$outFile = '.\group_members_{0:yyyy-MMM-dd-ddd hh-mm}.csv' -f (Get-Date)
$result = foreach ($DL in $DL_array) {
# output an object with the name of the DL and its members
[PsCustomObject]#{
Group = $DL
Users = (($DL | Get-DistributionGroupMember).displayName |
Where-Object {$_ -match '\S'}) -join ','
}
}
$result | Export-Csv -Path $outFile -NoTypeInformation
Using calculated properties, you can also do this without a foreach loop:
$DL_List = 'C:\ps1\shared_mailboxes\groups.csv'
$DL_array = (Import-Csv -Path $DL_List).name
$outFile = '.\group_members_{0:yyyy-MMM-dd-ddd hh-mm}.csv' -f (Get-Date)
$DL_array | Select-Object #{Name = 'Group'; Expresssion = {$_}},
#{Name = 'Users'; Expresssion = {
(($_ | Get-DistributionGroupMember).displayName |
Where-Object {$_ -match '\S'}) -join ','
}} |
Export-Csv -Path $outFile -NoTypeInformation

How to add an append to my powershell script?

I want to add an append to my script. So if it should crash it gives me the data to this point and dont print like nothing.
Right now the use of the script is to filter a List by name and Date. After that it remove all names on the blacklist and only contains entries from the month i entered
[xml]$config = Get-Content -Path 'C:\Users\DZimmermann\Desktop\EVIM.Script\EVIM-Config.xml'
[xml]$blacklist = Get-Content -Path 'C:\Users\DZimmermann\Desktop\EVIM.Script\EVIM-Blacklist.xml'
#Names to filter
$BLN = $blacklist.Names
#Import Path
$info = Import-Csv $config.config.path.input -Delimiter ';'
$info | Format-Table
#from which month
#$dateCutoff = get-date "02.2020" -Format "MM.yyyy"
$dateCutoff = $config.config.date
$result = foreach($i in $info){
if(-Not($blacklist -contains $i.SCAN_USER)){
$entryDate = get-date $i.SCAN_DATE -Format "MM.yyyy"
if($entryDate -eq $dateCutoff){
$i
}
}
Write-Host $i.SCAN_DATE
}
#Export path
$result | Export-Csv $config.config.path.output -NoTypeInformation -Delimiter ';'
$dateCutoff
all my changeble vars are linkt with a config file so you dont have to edit the script every time.
Start-Transcript -Path "path to save the transcript" -Append
xml]$config = Get-Content -Path 'C:\Users\DZimmermann\Desktop\EVIM.Script\EVIM-Config.xml'
[xml]$blacklist = Get-Content -Path 'C:\Users\DZimmermann\Desktop\EVIM.Script\EVIM-Blacklist.xml'
#Names to filter
$BLN = $blacklist.Names
#Import Path
$info = Import-Csv $config.config.path.input -Delimiter ';' -Verbose
$info | Format-Table -Verbose
#from which month
#$dateCutoff = get-date "02.2020" -Format "MM.yyyy"
$dateCutoff = $config.config.date
$result = foreach($i in $info){
if(-Not($blacklist -contains $i.SCAN_USER)){
$entryDate = get-date $i.SCAN_DATE -Format "MM.yyyy"
if($entryDate -eq $dateCutoff){
$i
}
}
Write-Host $i.SCAN_DATE
}
#Export path
$result | Export-Csv $config.config.path.output -NoTypeInformation -Delimiter ';' -Append -Verbose
$dateCutoff
Stop-Transcript
thank you for your help but i think i got it :) My script looks now like this
[xml]$config = Get-Content -Path 'C:\Users\DZimmermann\Desktop\EVIM.Script\EVIM-Config.xml'
[xml]$blacklist = Get-Content -Path 'C:\Users\DZimmermann\Desktop\EVIM.Script\EVIM-Blacklist.xml'
#Names to filter
$BLN = $blacklist.Names
#Import Path
$info = Import-Csv $config.config.path.input -Delimiter ';'
$info | Format-Table
#from which month
#$dateCutoff = get-date "02.2020" -Format "MM.yyyy"
$dateCutoff = $config.config.date
$result = foreach($i in $info){
if(-Not($BLN -contains $i.SCAN_USER)){
$entryDate = Get-Date $i.SCAN_DATE -Format "MM.yyyy"
if($entryDate -eq $dateCutoff){
$i
}
}
$result | Out-File $config.config.path.output
Write-Host $i
$config.config.path.output + "\" + $info | Out-File -Append $config.config.path.output
}

Exporting Hashtable to CSV

I'm trying to write a Powershell script which will take a several very long space-separated files and export some columns to similarly-named CSV files.
I do have a successful version:
Foreach ($file in $files) {
$WriteString=""
$outfile = $path + "\" + ($file -replace ".{4}$") + ".csv"
Get-Content -Path $path"\"$file | Select-Object -Skip $lines | ForEach-Object{
$ValueArray = ($_ -split "\s+")
$WriteString += $ValueArray[1] + "," + $ValueArray[2] + "," + $ValueArray[3] + "`n"
}
Add-Content -Path $outfile -Value $Writestring
}
This works, but is extremely slow - it takes over 16 hours for the script to fully run. The main cause (I think) is adding to the string. I've tried improving this using a hashtable:
Foreach ($file in $files) {
$outfile = $path + "\" + ($file -replace ".{4}$") + ".csv"
$ParseLines = Get-Content -Path $path"\"$file | Select-Object -Skip $lines
$OutputData = ForEach ($Line in $ParseLines) {
$ValueArray = ($Line -split "\s+")
$Line | Select-Object $ValueArray[1], $ValueArray[2], $ValueArray[3]
}
$OutputData | Export-CSV -Path $outfile #-NoTypeInformation
}
However, this is only exporting one line of the hashtable:
#TYPE Selected.System.String
"636050.000","7429825.000","77.438"
,,
,,
,,
,,
,,
,,
If I change the last line to:
Set-Content -Path $outfile -Value $OutputData
then the output becomes:
#{636050.000=; 7429825.000=; 77.438=}
#{636075.000=; 7429825.000=; 75.476=}
#{636100.000=; 7429825.000=; 74.374=}
#{636125.000=; 7429825.000=; 73.087=}
#{636150.000=; 7429825.000=; 71.783=}
#{636175.000=; 7429825.000=; 70.472=}
I'm clearly doing something wrong with either the hashtable or Export-CSV, but I can't figure it out. Any help will be greatly appreciated.
As requested below, here's part of one source file. I cut out all non-data rows, and don't include headers in my output CSV, as the input program (that the CSV files go into) doesn't require them, and the outputs are self-evident (Not much chance of getting the X, Y and Z values wrong just by looking at the data).
*
* DEFINITION
* HEADER_VARIABLES 3
* QUALITIES C 16 0 key
* DATE C 12 0
* TIME C 12 0
* VARIABLES 4
* X F 12 3
* Y F 12 3
* Z F 12 3
* gcmaq0.drg F 12 3
*
* 1 2 3 4
*23456789012345678901234567890123456789012345678
* X| Y| Z| gcmaq0.drg|
*
* HEADER:QUALITIES 29Aug2018 13:53:16
636575.000 7429800.000 75.551 75.551
636600.000 7429800.000 77.358 77.358
636625.000 7429800.000 78.823 78.823
636650.000 7429800.000 80.333 80.333
636675.000 7429800.000 82.264 82.264
636700.000 7429800.000 84.573 84.573
636725.000 7429800.000 87.447 87.447
Avoid slow operations like appending to strings (or arrays) in a loop. Change this:
Get-Content -Path $path"\"$file |
Select-Object -Skip $lines |
ForEach-Object {
$ValueArray = ($_ -split "\s+")
$WriteString += $ValueArray[1] + "," + $ValueArray[2] + "," + $ValueArray[3] + "`n"
}
Add-Content -Path $outfile -Value $Writestring
into this:
Get-Content -Path "${path}\${file}" |
Select-Object -Skip $lines |
ForEach-Object {
($_ -split "\s+")[1..3] -join ','
} |
Set-Content -Path $outfile
Replace Set-Content with Add-Content if you actually want to append to an existing file.
Export-Csv works with objects. It expects properties and values - what you're providing (judging from the Set-Content results) is hashtable with keys only.
One way around this is to create an object and increment values from each line.
Foreach ($file in $files) {
$outfile = $path + "\" + ($file -replace ".{4}$") + ".csv"
$ParseLines = Get-Content -Path $path"\"$file | Select-Object -Skip $lines
ForEach ($Line in $ParseLines) {
$ValueArray = ($Line -split "\s+")
[array]$OutputData += [pscustomobject]#{
header1 = $ValueArray[1]
header2 = $ValueArray[2]
header3 = $ValueArray[3]
}
}
$OutputData | Export-CSV -Path $outfile #-NoTypeInformation
}
Not sure if this is the optimal way if you have very large files - am sure a regex guru can come up with something more efficient.
The solution above by Ansgar Wiechers worked best, but I also found a second way of doing it at this SO question. It uses a ArrayList to store the hashtable, then writes the ArrayList. This method is almost, but not quite as fast as Ansgar's solution. (About 10x faster than string method, vs 12x for regex method)
Foreach ($file in $files) {
[System.Collections.ArrayList]$collection = New-Object System.Collections.ArrayList($null)
$outfile = $path + "\" + ($file -replace ".{4}$") + ".csv"
$ParseLines = Get-Content -Path $path"\"$file | Select-Object -Skip $lines
$OutputData =#{}
ForEach ($Line in $ParseLines) {
$ValueArray = ($Line -split "\s+")
$OutputData.Easting = $ValueArray[1]
$OutputData.Northing = $ValueArray[2]
$OutputData.ZValue = $ValueArray[3]
$collection.Add((New-Object PSObject -Property $OutputData)) | Out-Null
}
$collection | Export-CSV -Path $outfile -NoTypeInformation
}

Manipulate CSV value without knowing column names [duplicate]

I want to process a csv file in powershell, but I don't know what the column headings in the CSV file will be when it is processed.
For example:
$path = "d:\scratch\export.csv"
$csv = Import-csv -path $path
foreach($line in $csv)
{
foreach ($head in $line | get-member | where-object {$_.MemberType -eq "NoteProperty"} | select Definition)
{
#pseudocode...
doSomething($head.columnName, $head.value)
}
}
How do I loop through the line in the csv file, getting the name of the column and the value? Or is there another way I should be doing this (like not using Import-csv)?
Import-Csv $path | Foreach-Object {
foreach ($property in $_.PSObject.Properties)
{
doSomething $property.Name, $property.Value
}
}
A slightly other way of iterating through each column of each line of a CSV-file would be
$path = "d:\scratch\export.csv"
$csv = Import-Csv -path $path
foreach($line in $csv)
{
$properties = $line | Get-Member -MemberType Properties
for($i=0; $i -lt $properties.Count;$i++)
{
$column = $properties[$i]
$columnvalue = $line | Select -ExpandProperty $column.Name
# doSomething $column.Name $columnvalue
# doSomething $i $columnvalue
}
}
so you have the choice: you can use either $column.Name to get the name of the column, or $i to get the number of the column
$header3 = #("Field_1","Field_2","Field_3","Field_4","Field_5")
Import-Csv $fileName -Header $header3 -Delimiter "`t" | select -skip 3 | Foreach-Object {
$record = $indexName
foreach ($property in $_.PSObject.Properties){
#doSomething $property.Name, $property.Value
if($property.Name -like '*TextWrittenAsNumber*'){
$record = $record + "," + '"' + $property.Value + '"'
}
else{
$record = $record + "," + $property.Value
}
}
$array.add($record) | out-null
#write-host $record
}

get-spweb how can i get today created items

I'm using the next Powershell Script to get items from Sharepoint List and export it
how can i edit this script to get only today created data or the data entered today
This is the code
$MyWeb = Get-SPWeb "http://ilike-eg.suz.itcgr.net/SM"
$MyList = $MyWeb.Lists["SCGC"]
$exportlist = #()
$Mylist.Items | ForEach-Object {
$obj = New-Object PSObject -property #{
"A"=" "+$_["AACCOUNT_ID"]
"B"=" "+$_["BTRANSACTION_ID"]
"C"=" "+$_["CDATE"]
"D"=" "+$_["DCUSTOMER_ID"]
"E"=" "+$_["ECUSTOMER_NAME"]
"F"=" "+$_["FAMOUNT"]
"G"=" "+$_["GCLASS"]
}
#Remove unnecessary sort
$exportlist += $obj
$DateStamp = get-date -uformat "%Y-%m-%d#%H-%M-%S"
$NameOnly = "CDP"
#Exporting with sorted properties
$exportlist |
Select-Object A,B,C,D,E,F,G |
Export-Csv -Delimiter "`t"-path "$NameOnly.txt"
}
#Removed duplicate get-content line
$a, ${d:CDP.txt} = Get-Content .\CDP.txt
$a, ${d:CDP.txt} = Get-Content .\CDP.txt
(Get-Content D:\CDP.txt) |
Foreach-Object {$_ -replace $([char]34) -replace "`t" -replace '/', '-'} |
Set-Content D:\CDP.txt
Thanks
Where you're piping in the SPListItems, insert a query to get items created today
Change this:
$Mylist.Items |
To this:
$Mylist.Items | where {$_['Created'] -eq Get-Date -format d}