Trouble importing CSV and then exporting with some manipulation - powershell

I have the following file:
2018|CIB_C21_A_1_1_FromRangeAmount|.0000
As you can see, it has no headers. The first column can be ignored. I need to take the 2nd column and then export but I also need to add (2) additional columns in the export.
The export should look like this:
Account,Parent,Data Storage
CIB_C21_A_1_1|CIB_C21_A|Never Share
As you can see, I need to take the first column and also use the first 9 chracters as a value for another column. Then I need to add another column with the same value at all times.
I have been trying to play around with this some of this logic but can't quote any anything to work...
Import-Csv -Path "C:\TEMPCT\Revenue_RebatesDataForPlanning.csv" -Header zz,Account -Delim "|" |
sort "Account" –Unique |
Select "Account", "Parent" |
Export-Csv -Path "C:\TEMPCT\Revenue_RebatesDataForPlanningzz.csv" -Force -NoTypeInformation
Could anyone recommend some suggestions?

You can use calculated properties with Select-Object to create the desired new columns. To get the 9 first characters of the value from "Account" column you can use the String.Substring Method.
# This would be:
# Import-Csv path/to/csv.csv -Header Ignore, Account -Delimiter '|' | ...
#'
2018|CIB_C21_A_1_1_FromRangeAmount|.0000
2018|CIB_C22_A_1_1_FromRangeAmount|.0000
2018|CIB_C23_A_1_1_FromRangeAmount|.0000
2018|CIB_C24_A_1_1_FromRangeAmount|.0000
2018|CIB_C25_A_1_1_FromRangeAmount|.0000
'# | ConvertFrom-Csv -Header Ignore, Account -Delimiter '|' |
Select-Object Account, #{
Name = 'Parent'
Expression = { $_.Account.Substring(0,9) }
}, #{
Name = 'Data Storage'
Expression = { 'Never Share' }
} | ConvertTo-Csv -NoTypeInformation
How your code should look:
Import-Csv path/to/csv.csv -Header Ignore, Account -Delimiter '|' |
Select-Object Account, #{
Name = 'Parent'
Expression = { $_.Account.Substring(0,9) }
}, #{
Name = 'Data Storage'
Expression = { 'Never Share' }
} | Export-Csv path/to/newCsv.csv -NoTypeInformation

Related

Powershell script to Find and replace Particular value in CSV file

I have a csv file in path and i need to fetch the file and do Find & Replace $(dollar) value to ,(comma) using PowerShell script. please help me on the script .
CSV file
id|name|place
1|adam$|USA
2|john|USA
3|Jack$|England
Expected output : Need to do Find & Replace $(dollar) value to ,(comma) under name column
id|name|place
1|adam,|USA
2|john|USA
3|Jack,|England
Command Used
Import-csv D:\CSV\Customer.csv
ForEach-Object { $_.name -replace '$',',' }
i didn't got any error nor the expected changes. i need to update the values in existing CSV file
Below Answer by Zett42 works but
(Import-Csv D:\CSV\Customer.csv -Delimiter '|') |
Select-Object 'id',
#{ n = 'name'; e = { $_.name.Replace('$', ',') } },
'place' |
Export-Csv D:\CSV\Customer.csv -Delimiter '|'
but the tricky part is we are replacing the ,(Comma) value and rewriting the CSV files again, so CSV file creation part considers the value after ,(comma) as new column and move the value to column B instead of Column A.
Before :
After :
This should do the trick:
Import-Csv D:\CSV\Customer.csv -Delimiter '|' |
ForEach-Object {
$_.name = $_.name.Replace('$', ',')
$_ # Implicit output - gets passed to Export-Csv
} |
Export-Csv D:\CSV\Customer_new.csv -Delimiter '|' -NoTypeInformation
In your code, there is a pipe symbol | missing at the end of the Import-Csv line.
As the CSV uses non-standard delimiters, you have to specify them using -Delimiter.
Also, the -replace operator doesn't modify its arguments, so you are actually just outputting the replaced value. Also, '$' has special meaning in regular expression patterns, which -replace uses, so you have to backslash-escape it or use the String.Replace() method as I did in the code above. The String.Replace() method doesn't use RegEx.
In the ForEach-Object script block, the $_ at the end is necessary to output the current object again, so it gets send down the pipeline to Export-Csv. Otherwise Export-Csv wouldn't have anything to output.
Alternative code using Select-Object:
Import-Csv D:\CSV\Customer.csv -Delimiter '|' |
Select-Object 'id',
#{ n = 'name'; e = { $_.name.Replace('$', ',') } },
'place' |
Export-Csv D:\CSV\Customer_new.csv -Delimiter '|' -NoTypeInformation
To overwrite the existing file instead of creating a new file use the group operator () to collect all data in memory and close the old file before overwriting the file:
(Import-Csv D:\CSV\Customer.csv -Delimiter '|') |
Select-Object 'id',
#{ n = 'name'; e = { $_.name.Replace('$', ',') } },
'place' |
Export-Csv D:\CSV\Customer.csv -Delimiter '|' -NoTypeInformation
IMO this is somewhat cleaner as you don't need to modify the current pipeline object and don't need to explicitly forward it to the next command.
It uses a calculated property to generate the new value for the name column. In this case we don't need to assign the result back to $_.name because calculated properties automatically assign the output from the script block to the property.

Change value in column using powershell

I have a csv file with five columns. The last column being the students grade level. What I want to do is change the value of that column based on the gradelevel. For example if the gradelevel is 12 I want to change that to 2016, 11 to 2017, so on and so forth.
update: I did get it to semi work using the below:
Get-Content users.csv | ForEach-Object -Process {$_ -replace '12','2016'} | Set-Content users1.csv
What happens is if the student id has a 12 in it that gets changed as well to 2016. Example would be 120045 gets change to 20160045
You can import the csv, loop it in a foreach and use the $_ (this) operator and then export ist to csv
Somesthing like:
# import CSV
$list = import-csv P:\ath\to\file.csv -Delimiter ";" -Encoding Standard
# % means foreach
$list | % {
# The grades is the key for each line
# the first line of your csv represent the keys you can use
# e.g. first colum: Name | Grade | Class
# $_.Name | $_.Grade | $_.Class are your keys for every entry (2nd line and above)
# here you can work with your grades an do what you want
if($_.Grade -eq 11){
# set a new value to the grade in the actual line
$_.Grade = 2016
}
}
# now export the new list into csv
export-csv P:\ath\to\new.csv -Delimiter ";" -NoTypeInformation
That should be a basic to work with.
Greetz Eldo.O
Eldo you code worked great. I did have to change the last line from:
export-csv P:\ath\to\new.csv -Delimiter ";" -NoTypeInformation
to
$list | Export-Csv P:\ath\to\new.csv -Delimiter "," -NoTypeInformation
I was also able to add more if statements to accomplish exactly what I needed.

How to detect and remove CSV columns based on common text in header names?

I am working on a CSV File which I recently created. The CSV file contains columns with headers and corresponding rows.
I need to remove entire columns (including its data) that have specific text common to their headers. For e.g column 1 has header named intID, column 2 has header named boolID, column 3 has header named charID and so on ('ID' being the common text). There are some columns that don't have 'ID' as text in their headers, so we need to retain those.
The csv file is getting generated dynamically, so there may be more/less columns based on what data we select for the csv. But we need these columns with their headers having some common text to be removed.
How can we achieve this?
Would something like that do the trick?
$yourfile = "<path to your csv>"
# Import the CSV
$csv = Import-Csv -Path $yourfile
# Find all columns that do not end with "ID"
$colsToKeep = $csv | Get-Member -MemberType NoteProperty |?{$_.name -notmatch "^.+ID$"} | Select-Object -ExpandProperty name
# Filter out all unwanted columns
$newCsv = $csv | Select-Object -Property $colsToKeep
# Export CSV to new file
$newCsv | Export-Csv -Path "<path to new csv>"
Assuming the following:
the ID part is not a plain text "ID" but a dynamic arbitrary text
headers of interest start with int, char, bool
Let's count occurrences of ID part and build a list of headers used just once, then export the CSV.
$csv = Import-Csv 1.csv
$prefix = '^(int|char|bool)' # or '^([a-z])' for any lowercase text
$headers = $csv[0].PSObject.Properties.Name
$uniqueIDs = $headers -creplace $prefix, '' | group | ? Count -eq 1 | select -expand Name
$uniqueHeaders = $headers | ?{ $_ -creplace $prefix, '' -in $uniqueIDs }
$csv | select $uniqueHeaders | Export-Csv 2.csv -NoTypeInformation
Note: in the old PowerShell 2.0 instead of ? Count -eq 1 use ?{ $_.Count -eq 1 }

Use Import-Csv to read changable column Titles by location

I'm trying to see if there is a way to read the column values in a csv file based on the column location. The reason for this is the file I'm being handed always has it's titles being changed...
For example, lets say csv file column A (via excel) looks like the following:
ColumnOne
ValueOne
ValueTwo
ValueThree
Now the user changes the title:
Column 1
ValueOne
ValueTwo
ValueThree
Now I want to create an array of the first column. Normally what I do is the following:
$arrayFirstColumn = Import-Csv 'C:\test\test1.csv' | where-object {$_.ColumnOne} | select-object -expand 'ColumnOne'
However, as we can see if ColumnOne is changed to Column 1, it breaks this code. How can I create this array to allow an interchangeable column title, but the column location will always be the same?
You can specify headers of your own on import:
Import-Csv 'C:\path\to\your.csv' -Header 'MyHeaderA','MyHeaderB',...
As long as you don't export the data back to a CSV (or don't require the original headers to be in the output CSV as well) you can use whatever names you like. You can also specify as many header names as you like. If their number is less than the number of the columns in the CSV the additional columns will be omitted, if it's greater then the columns for the additional headers will be empty.
If you need to preserve the original headers you could get the header name(s) you need to work with in variable(s) like this:
$csv = Import-Csv 'C:\test\test1.csv'
$firstCol = $csv | Select-Object -First 1 | ForEach-Object {
$_.PSObject.Properties | Select-Object -First 1 -Expand Name
}
$arrayFirstColumn = $csv | Where-Object {$_.$firstCol} |
Select-Object -Expand $firstCol
Or you could simply read the first line from the CSV and split it to get an array with the headers:
$headers = (Get-Content 'C:\test\test1.csv' -TotalCount 1) -split ','
$firstCol = $headers[0]
One option:
$ImportFile = 'C:\test\test1.csv'
$FirstColumn = ((Get-Content $ImportFile -TotalCount 2 | ConvertFrom-Csv).psobject.properties.name)[0]
$FirstColumn
$arrayFirstColumn = Import-Csv $ImportFile | where-object {$_.$FirstColumn} | select-object -expand $FirstColumn
If you are using PowerShell v2.0 then the expression for $FirstColumn in $mjolinor's answer would be:
$FirstColumn = ((Get-Content $ImportFile -TotalCount 2 | ConvertFrom-Csv).psobject.properties | ForEach-Object {$_.name})[0]
(Apologies for starting a new answer; I do not yet have enough reputation to add a comment to mjolinor's post)

How can I increase the value of a variable in an expression?

I have the following code:
$b = 1
Import-Csv c:\Awsaf\powershell\Beforenew.csv | select name, CustomAttribute1, CustomAttribute2, #{n='Counter';e={$b}} | Export-Csv -NoTypeInformation c:\Awsaf\PowerShell\afternew.csv
I want to increment the value of $b by 1. I have tried $b++, $b+=, for loop, do-while, nothing seems to be working. How can I do it?
I have also tried the following additional code, but I cannot figure out how to increase the value of $b.
$b = 0
Import-CSV c:\Awsaf\powershell\afternew.csv -Delimiter ',' | `
ForEach-Object { $_.Counter = "$b"; return $_ } | `
Export-CSV c:\awsaf\powershell\afterX.csv -Delimiter ',' -NoTypeInformation
Use explicit script scope for the variable 'b'. After increment (++) do not forget to output the value.
Here is the code that works fine for me (it is a slightly changed version of yours):
# Prepare some data for the test
Get-ChildItem | Select-Object Name | Export-Csv test1.csv -NoTypeInformation
# 1) Use explicit script scope for the variable 'b'
# 2) After increment (++) do not forget to output it
$script:b = 0
Import-Csv test1.csv |
select Name, #{n='Counter'; e={$script:b++; $script:b}} |
Export-Csv -NoTypeInformation test2.csv
This is a slight variation as I needed this in a string. Roman's curly brackets don't take a string. You need to use parentheses:
`"$($script:b++; $script:b)"`
Ohter option (although not so beautiful) is to use Add-Member. You tried it in your ForEach-Object:
gci |
select Name |
% -beg {$counter=0} {$counter++; Add-Member -in $_ -name Test NoteProperty $counter -pass }