How to add quotes and commas in PowerShell? - powershell

I have a CSV file where I only need 1 Column Called "SerialNumber" I need to combine the text lines, remove any blank space, add each line in quotes and separate by comma.
So far I have multiple miles of code that work, but it adds quotes at the end and doesn't add quotes in the beginning.
$SerialList = import-csv .\log.csv | select -ExpandProperty Serialnumber | Out-File -FilePath .\Process.txt
(gc process.txt) | ? {$_.trim() -ne "" } | set-content process.txt
gc .\process.txt | %{$_ -replace '$','","'} | out-file process1.csv
Get-Content .\process1.csv| foreach {
$out = $out + $_
}
$out| Out-File .\file2.txt
Output:
SerialNumber
1234
1234
4567
4567
Expected Output:
"1234","1234","4567","4567"

Try the following (PSv3+):
(Import-Csv .\log.csv).SerialNumber -replace '^.*$', '"$&"' -join "," > .\file2.txt
(Import-Csv .\log.csv).SerialNumber imports the CSV file and .SerialNumber uses member-access enumeration to extract the SerialNumber column values as an array.
-replace '^.*$', '"$&"' encloses each array element in "...".
Regex ^.*$ matches each array element in full.
Replacement expression "$&" replaces the element with what was matched ($&) enclosed in " chars. - for background, see this answer
-join "," joins the resulting array elements with , as the separator.

Related

How to replace comma with pipe?

I am in the situation where I need to replace the comma with pipe using PowerShell. the fields are within double quotes so I need to remove them as well. but Some fields have comma in the data so When I replace, I should keep fields commas with while space.
Data look like this:
"Whaling, Mark","faflitto#cgf.com"
I want data to be like this:
Whaling Mark |faflitto#cgf.com
How to achieve this using PowerShell?. Help appreciated.
My script is this for now:
(Get-Content -ReadCount 0 Compliance2022.txt) -replace ',','|' -replace '"',' ' | Set-Content COMPLIANCE2022_.txt
I did NOT test this whole line, just the code between the Get-Content and Set-Content commands. The regex came from here.
(Get-Content -ReadCount 0 Compliance2022.txt) | ForEach-Object {($_ -Split '(?!\B"[^"]*),(?![^"]*"\B)') -Join(' | ')} | Set-Content COMPLIANCE2022_.txt
Odds are it misses exactly what you want, but maybe we can fine tune it to get to work.
EDIT:
This version removes the quotes.
(Get-Content -ReadCount 0 Compliance2022.txt) | ForEach-Object {(($_ -Split '(?!\B"[^"]*),(?![^"]*"\B)') -Join(' | ')) -replace '"', " "} | Set-Content COMPLIANCE2022_.txt

Import 1 column of csv, replace string, then join each row into one row with values surrounded in quotes

Im using powershell and I have a csv file that has a ton of data but the only field I am concerned with is the first one.
I need to:
Extract the first column of data with the header: Ticket_ID
Remove a trailing string from the values in this column (site:)
Join all of the values in the column into a single, space delimited row, but ensure each value retains the double quotes wrapping each of them.
Prepend this row with a long string and write it to a file. (I havent tried to tackle this part yet because I cant get 1-3 to work fully.)
I have tried several permutations of this code:
$TT = (Import-Csv '.\sourcefile.csv').______Ticket_ID______ -join (" ")
$TT = $TT -replace 'site:',''
$TT | Set-Content -Path .\JulyTickets.csv
Example of my csv:
"______Ticket_ID______","____________Title____________","___________CreateDate___________"
"site:TICKET1","SOMETITLE","01/01/1901"
"site:TICKET2","SOMETITLE","01/01/1901"
"site:TICKET3","SOMETITLE","01/01/1901"
"site:TICKET4","SOMETITLE","01/01/1901"
"site:TICKET5","SOMETITLE","01/01/1901"
My frustrations:
If I use Export-CSV the resulting csv only contains the character length of the values.
If I use Set-Content, I can successfully extract the first column, and remove the leading string (site:), join all of the rows into a single row, and write this out to a file but it does not retain the double quotes around each of the values in the csv ("site:TICKET1"). And the output looks like this:
TICKET1 TICKET2 TICKET3 TICKET4 TICKET5
I'm not sure if I'm doing things out of order here, or if I'm missing something but I'm unable to get both the values inline, and individually surrounded in quotes.
Required Output:
this is a long string that needs to be prepended to the data "TICKET1" "TICKET2" "TICKET3" "TICKET4" "TICKET5"
This should do it. The need for pipes and PowerShell's Calculated Properties expression in the end is to overcome powershell removing double quotes
$string = 'this is a long string that needs to be prepended to the data ' + (Get-Content -Path \.sourcefile.csv | Convertfrom-Csv -Delimiter "," | select ______Ticket_ID______ -ExpandProperty ______Ticket_ID______ | ForEach-Object {$_ -replace 'site:', '';} | select #{ Name = 'Ticket'; Expression = {$([char]34) + $_ + $([char]34)}} | Select Ticket -ExpandProperty Ticket);
write-host $string
returns
this is a long string that needs to be prepended to the data "TICKET1" "TICKET2" "TICKET3" "TICKET4" "TICKET5"
Try this out, I'm using the -Header argument since the only needed column is the first one.
$csv = #'
"______Ticket_ID______","____________Title____________","___________CreateDate___________"
"site:TICKET1","SOMETITLE","01/01/1901"
"site:TICKET2","SOMETITLE","01/01/1901"
"site:TICKET3","SOMETITLE","01/01/1901"
"site:TICKET4","SOMETITLE","01/01/1901"
"site:TICKET5","SOMETITLE","01/01/1901"
'# |
ConvertFrom-Csv -Header ID |
Select-Object -Skip 1 |
ForEach-Object {
'"{0}"' -f $_.ID.TrimStart('site:')
}
$toPrepend = 'this is a long string that needs to be prepended to the data'
"$toPrepend $($csv -join ' ')"
# Above yields => this is a long string that needs to be prepended to the data "TICKET1" "TICKET2" "TICKET3" "TICKET4" "TICKET5"
Note, I'm only using ConvertFrom-Csv as an example here but in your case, it should look like this:
$TT = Import-Csv '.\sourcefile.csv' -Header ID | Select-Object -Skip 1 | ForEach-Object {
'"{0}"' -f $_.ID.TrimStart('site:')
}
$toPrepend = 'this is a long string that needs to be prepended to the data'
"$toPrepend $($TT -join ' ')" | Out-File JulyTickets.csv # => Not sure why CSV here, since this is no longer a CSV

Powershell Remove spaces in the header only of a csv

First line of csv looks like this spaces are at after Path as well
author ,Revision ,Date ,SVNFolder ,Rev,Status,Path
I am trying to remove spaces only and rest of the content will be the same .
author,Revision,Date,SVNFolder,Rev,Status,Path
I tried below
Import-CSV .\script.csv | ForEach-Object {$_.Trimend()}
expanding on the comment with an example since it looks like you may be new:
$text = get-content .\script.csv
$text[0] = $text[0] -replace " ", ""
$csv = $text | ConvertFrom-CSV
Note: The solutions below avoid loading the entire CSV file into memory.
First, get the header row and fix it by removing all whitespace from it:
$header = (Get-Content -TotalCount 1 .\script.csv) -replace '\s+'
If you want to rewrite the CSV file to fix its header problem:
# Write the corrected header and the remaining lines to the output file.
# Note: I'm outputting to a *new* file, to be safe.
# If the file fits into memory as a whole, you can enclose
# Get-Content ... | Select-Object ... in (...) and write back to the
# input file, but note that there's a small risk of data loss, if
# writing back gets interrupted.
& { $header; Get-Content .\script.csv | Select-Object -Skip 1 } |
Set-content -Encoding utf8 .\fixed.csv
Note: I've chosen -Encoding utf8 as the example output character encoding; adjust as needed; note that the default is ASCII(!), which can result in data loss.
If you just want to import the CSV using the fixed headers:
& { $header; Get-Content .\script.csv | Select-Object -Skip 1 } | ConvertFrom-Csv
As for what you tried:
Import-Csv uses the column names in the header as property names of the custom objects it constructs from the input rows.
This property names are locked in at the time of reading the file, and cannot be changed later - unless you explicitly construct new custom objects from the old ones with the property names trimmed.
Import-Csv ... | ForEach-Object {$_.Trimend()}
Since Import-Csv outputs [pscustomobject] instances, reflected one by one in $_ in the ForEach-Object block, your code tries call .TrimEnd() directly on them, which will fail (because it is only [string] instances that have such a method).
Aside from that, as stated, your goal is to trim the property names of these objects, and that cannot be done without constructing new objects.
Read the whole file into an array:
$a = Get-Content test.txt
Replace the spaces in the first array element ([0]) with empty strings:
$a[0] = $a[0] -replace " ", ""
Write over the original file: (Don't forget backups!)
$a | Set-Content test.txt
$inFilePath = "C:\temp\headerwithspaces.csv"
$content = Get-Content $inFilePath
$csvColumnNames = ($content | Select-Object -First 1) -Replace '\s',''
$csvColumnNames = $csvColumnNames -Replace '\s',''
$remainingFile = ($content | Select-Object -Skip 1)

How to avoid double quote when using export-csv in Powershell [duplicate]

I am using ConvertTo-Csv to get comma separated output
get-process | convertto-csv -NoTypeInformation -Delimiter ","
It outputs like:
"__NounName","Name","Handles","VM","WS",".....
However I would like to get output without quotes, like
__NounName,Name,Handles,VM,WS....
Here is a way to remove the quotes
get-process | convertto-csv -NoTypeInformation -Delimiter "," | % {$_ -replace '"',''}
But it has a serious drawback if one of the item contains a " it will be removed !
Hmm, I have Powershell 7 preview 1 on my mac, and Export-Csv has a -UseQuotes option that you can set to AsNeeded. :)
I was working on a table today and thought about this very question as I was previewing the CSV file in notepad and decided to see what others had come up with. It seems many have over-complicated the solution.
Here's a real simple way to remove the quote marks from a CSV file generated by the Export-Csv cmdlet in PowerShell.
Create a TEST.csv file with the following data.
"ID","Name","State"
"5","Stephanie","Arizona"
"4","Melanie","Oregon"
"2","Katie","Texas"
"8","Steve","Idaho"
"9","Dolly","Tennessee"
Save As: TEST.csv
Store file contents in a $Test variable
$Test = Get-Content .\TEST.csv
Load $Test variable to see results of the get-content cmdlet
$Test
Load $Test variable again and replace all ( "," ) with a comma, then trim start and end by removing each quote mark
$Test.Replace('","',",").TrimStart('"').TrimEnd('"')
Save/Replace TEST.csv file
$Test.Replace('","',",").TrimStart('"').TrimEnd('"') | Out-File .\TEST.csv -Force -Confirm:$false
Test new file Output with Import-Csv and Get-Content:
Import-Csv .\TEST.csv
Get-Content .\TEST.csv
To Sum it all up, the work can be done with 2 lines of code
$Test = Get-Content .\TEST.csv
$Test.Replace('","',",").TrimStart('"').TrimEnd('"') | Out-File .\TEST.csv -Force -Confirm:$false
I ran into this issue, found this question, but was not satisfied with the answers because they all seem to suffer if the data you are using contains a delimiter, which should remain quoted. Getting rid of the unneeded double quotes is a good thing.
The solution below appears to solve this issue for a general case, and for all variants that would cause issues.
I found this answer elsewhere, Removing quotes from CSV created by PowerShell, and have used it to code up an example answer for the SO community.
Attribution: Credit for the regex, goes 100% to Russ Loski.
Code in a Function, Remove-DoubleQuotesFromCsv
function Remove-DoubleQuotesFromCsv
{
param (
[Parameter(Mandatory=$true)]
[string]
$InputFile,
[string]
$OutputFile
)
if (-not $OutputFile)
{
$OutputFile = $InputFile
}
$inputCsv = Import-Csv $InputFile
$quotedData = $inputCsv | ConvertTo-Csv -NoTypeInformation
$outputCsv = $quotedData | % {$_ -replace `
'\G(?<start>^|,)(("(?<output>[^,"]*?)"(?=,|$))|(?<output>".*?(?<!")("")*?"(?=,|$)))' `
,'${start}${output}'}
$outputCsv | Out-File $OutputFile -Encoding utf8 -Force
}
Test Code
$csvData = #"
id,string,notes,number
1,hello world.,classic,123
2,"a comma, is in here","test data 1",345
3,",a comma, is in here","test data 2",346
4,"a comma, is in here,","test data 3",347
5,"a comma, is in here,","test data 4`r`nwith a newline",347
6,hello world2.,classic,123
"#
$data = $csvData | ConvertFrom-Csv
"`r`n---- data ---"
$data
$quotedData = $data | ConvertTo-Csv -NoTypeInformation
"`r`n---- quotedData ---"
$quotedData
# this regular expression comes from:
# http://www.sqlmovers.com/removing-quotes-from-csv-created-by-powershell/
$fixedData = $quotedData | % {$_ -replace `
'\G(?<start>^|,)(("(?<output>[^,"\n]*?)"(?=,|$))|(?<output>".*?(?<!")("")*?"(?=,|$)))' `
,'${start}${output}'}
"`r`n---- fixedData ---"
$fixedData
$fixedData | Out-File e:\test.csv -Encoding ascii -Force
"`r`n---- e:\test.csv ---"
Get-Content e:\test.csv
Test Output
---- data ---
id string notes number
-- ------ ----- ------
1 hello world. classic 123
2 a comma, is in here test data 1 345
3 ,a comma, is in here test data 2 346
4 a comma, is in here, test data 3 347
5 a comma, is in here, test data 4... 347
6 hello world2. classic 123
---- quotedData ---
"id","string","notes","number"
"1","hello world.","classic","123"
"2","a comma, is in here","test data 1","345"
"3",",a comma, is in here","test data 2","346"
"4","a comma, is in here,","test data 3","347"
"5","a comma, is in here,","test data 4
with a newline","347"
"6","hello world2.","classic","123"
---- fixedData ---
id,string,notes,number
1,hello world.,classic,123
2,"a comma, is in here",test data 1,345
3,",a comma, is in here",test data 2,346
4,"a comma, is in here,",test data 3,347
5,"a comma, is in here,","test data 4
with a newline","347"
6,hello world2.,classic,123
---- e:\test.csv ---
id,string,notes,number
1,hello world.,classic,123
2,"a comma, is in here",test data 1,345
3,",a comma, is in here",test data 2,346
4,"a comma, is in here,",test data 3,347
5,"a comma, is in here,","test data 4
with a newline","347"
6,hello world2.,classic,123
This is pretty similar to the accepted answer but it helps to prevent unwanted removal of "real" quotes.
$delimiter = ','
Get-Process | ConvertTo-Csv -Delimiter $delimiter -NoTypeInformation | foreach { $_ -replace '^"','' -replace "`"$delimiter`"",$delimiter -replace '"$','' }
This will do the following:
Remove quotes that begin a line
Remove quotes that end a line
Replace quotes that wrap a delimiter with the delimiter alone.
Therefore, the only way this would go wrong is if one of the values actually contained not only quotes, but specifically a quote-delimiter-quote sequence, which hopefully should be pretty uncommon.
Once the file is generated, you can run
set-content FILENAME.csv ((get-content FILENAME.csv) -replace '"')
Depending on how pathological (or "full-featured") your CSV data is, one of the posted solutions will already work.
The solution posted by Kory Gill is almost perfect - the only issue remaining is that quotes are removed also for cells containing the line separator \r\n, which is causing issues in many tools.
The solution is adding a newline to the character class expression:
$fixedData = $quotedData | % {$_ -replace `
'\G(?<start>^|,)(("(?<output>[^,"\n]*?)"(?=,|$))|(?<output>".*?(?<!")("")*?"(?=,|$)))' `
,'${start}${output}'}
I wrote this for my needs:
function ConvertTo-Delimited {
[CmdletBinding()]
param(
[Parameter(ValueFromPipeline=$true,Mandatory=$true)]
[psobject[]]$InputObject,
[string]$Delimiter='|',
[switch]$ExcludeHeader
)
Begin {
if ( $ExcludeHeader -eq $false ) {
#(
$InputObject[0].PsObject.Properties | `
Select-Object -ExpandProperty Name
) -Join $Delimiter
}
}
Process {
foreach ($item in $InputObject) {
#(
$item.PsObject.Properties | `
Select-Object Value | `
ForEach-Object {
if ( $null -ne $_.Value ) {$_.Value.ToString()}
else {''}
}
) -Join $Delimiter
}
}
End {}
}
Usage:
$Data = #(
[PSCustomObject]#{
A = $null
B = Get-Date
C = $null
}
[PSCustomObject]#{
A = 1
B = Get-Date
C = 'Lorem'
}
[PSCustomObject]#{
A = 2
B = Get-Date
C = 'Ipsum'
}
[PSCustomObject]#{
A = 3
B = $null
C = 'Lorem Ipsum'
}
)
# with headers
PS> ConvertTo-Delimited $Data
A|B|C
1|7/17/19 9:07:23 PM|Lorem
2|7/17/19 9:07:23 PM|Ipsum
||
# without headers
PS> ConvertTo-Delimited $Data -ExcludeHeader
1|7/17/19 9:08:19 PM|Lorem
2|7/17/19 9:08:19 PM|Ipsum
||
Here's another approach:
Get-Process | ConvertTo-Csv -NoTypeInformation -Delimiter "," |
foreach { $_ -replace '^"|"$|"(?=,)|(?<=,)"','' }
This replaces matches with the empty string, in each line. Breaking down the regex above:
| is like an OR, used to unite the following 4 sub-regexes
^" matches quotes in the beginning of the line
"$ matches quotes in the end of the line
"(?=,) matches quotes that are immediately followed by a comma
(?<=,)" matches quotes that are immediately preceded by a comma
I found that Kory's answer didn't work for the case where the original string included more than one blank field in a row. I.e. "ABC",,"0" was fine but "ABC",,,"0" wasn't handled properly. It stopped replacing quotes after the ",,,". I fixed it by adding "|(?<output>)" near the end of the first parameter, like this:
% {$_ -replace `
'\G(?<start>^|,)(("(?<output>[^,"]*?)"(?=,|$))|(?<output>".*?(?<!")("")*?"(?=,|$))|(?<output>))', `
'${start}${output}'}
I haven't spent much time looking for removing the quotes. But, here is a workaround.
get-process | Export-Csv -NoTypeInformation -Verbose -Path $env:temp\test.csv
$csv = Import-Csv -Path $env:temp\test.csv
This is a quick workaround and there may be a better way to do this.
A slightly modified variant of JPBlanc's answer:
I had an existing csv file which looked like this:
001,002,003
004,005,006
I wanted to export only the first and third column to a new csv file. And for sure I didn't want any quotes ;-)
It can be done like this:
Import-Csv -Path .\source.csv -Delimiter ',' -Header A,B,C | select A,C | ConvertTo-Csv -NoTypeInformation -Delimiter ',' | % {$_ -replace '"',''} | Out-File -Encoding utf8 .\target.csv
Couldn't find an answer to a similar question so I'm posting what I've found here...
For exporting as Pipe Delimited with No Quotes for string qualifiers, use the following:
$objtable | convertto-csv -Delimiter "|" -notypeinformation | select -Skip $headers | % { $_ -replace '"\|"', "|"} | % { $_ -replace '""', '"'} | % { $_ -replace "^`"",''} | % { $_ -replace "`"$",''} | out-file "$OutputPath$filename" -fo -en ascii
This was the only thing I could come up with that could handle quotes and commas within the text; especially things like a quote and comma next to each other at the beginning or ending of a text field.
This function takes a powershell csv object from the pipeline and outputs like convertto-csv but without adding quotes (unless needed).
function convertto-unquotedcsv {
param([Parameter(ValueFromPipeline=$true)]$csv, $delimiter=',', [switch]$noheader=$false)
begin {
$NeedQuotesRex = "($([regex]::escape($delimiter))|[\n\r\t])"
if ($noheader) { $names = #($true) } else { $names = #($false) }
}
process {
$psop = $_.psobject.properties
if (-not $names) {
$names = $psop.name | % {if ($_ -match $NeedQuotesRex) {'"' + $_ + '"'} else {$_}}
$names -join $delimiter # unquoted csv header
}
$values = $psop.value | % {if ($_ -match $NeedQuotesRex) {'"' + $_ + '"'} else {$_}}
$values -join $delimiter # unquoted csv line
}
end {
}
}
$names gets an array of noteproperty names and $values gets an array of notepropery values. It took that special step to output the header. The process block gets the csv object one piece at a time.
Here is a test run
$delimiter = ','; $csvData = #"
id,string,notes,"points per 1,000",number
4,"a delimiter$delimiter is in here,","test data 3",1,348
5,"a comma, is in here,","test data 4`r`nwith a newline",0.5,347
6,hello world2.,classic,"3,000",123
"#
$csvdata | convertfrom-csv | sort number | convertto-unquotedcsv -delimiter $delimiter
id,string,notes,"points per 1,000",number
6,hello world2.,classic,"3,000",123
5,"a comma, is in here,","test data 4
with a newline",0.5,347
4,"a delimiter, is in here,",test data 3,1,348

powershell script to change column position in text file

Do you know how to use powershell to move column DocumentNo to last column ?
| D|Ref.Doc. |Row|DocumentNo |CoCd|Pstng Date
| W|5007534739| 1|65713191 |STCD|01/17/2016
| W|5007534739| 1|65713191 |STCD|01/17/2016
| W|5007534739| 1|65713191 |STCD|01/17/2016
Expected output
| D|Ref.Doc. |Row|CoCd|Pstng Date|DocumentNo
| W|5007534739| 1|STCD|01/17/2016|65713191
| W|5007534739| 1|STCD|01/17/2016|65713191
| W|5007534739| 1|STCD|01/17/2016|65713191
Here the command that I've tried
(get-content $file -ReadCount 0) |
foreach {
'{0}|{1}|{2}|{3}|{5}|{6}|{4}' -f $_.split('|')
} | Set-Content $file2
The code works properly, but in case DocumentNo containt a pipe as data, how to handle it ?
A delimeter is no good if it's used inside the data itself as it breaks the csv. I see that the data is fixed width (though the length and index may change from file to file), so I would take a different apporach.
Find the location for the DocumentNo-column. You can use regex for this (https://regex101.com/r/pH2oL9/1). I use [Regex]::Match() because it returns index (start position) and length (number of characters) of a match (the column).
Create a regex that finds the content at the same position and length on every line. It makes groups for "before", "content", "after" by counting "any character" until the start position I've decided, then the length of the column, then "until end of line". I use subexpressions $() in the regex to insert the index and length from step 2 since they might not be the same for every file.
Use -replace with the generated regex to modify the text on each line (since $text is an array). -replace finds the groups, and by using $1$3$2 I can say which order I want the groups inserted in the result. (https://regex101.com/r/vE6vO9/1)
Solution:
#Sample text
$text = Get-Content .\Test.txt
#Analyze header (find DocumentNo placement in fixed-width file) and create regex
$regex = [regex]::Match($text[0], '\|DocumentNo\s+') | ForEach-Object { "^(.{$($_.Index)})(.{$($_.Length)})(.*)$" }
#Modify text
$text -replace $regex, '$1$3$2' | Set-Content .\TestOut.txt
TestOut.txt
| D|Ref.Doc. |Row|CoCd|Pstng Date|DocumentNo
| W|5007534739| 1|STCD|01/17/2016|65713191
| W|5007534739| 1|STCD|01/17/2016|65713191
| W|5007534739| 1|STCD|01/17/2016|65713191
You might want to trim the trailing whitespaces. Use Trim() for this:
$text -replace $regex, '$1$3$2' | ForEach-Object { $_.Trim() } | Set-Content .\TestOut.txt
You could do the following:
$delimiter = "|"
$data = Get-Content "c:\tmp\test.csv";
$newCsv= ($data|Foreach-object { ($_ -split $delimiter)[#(0..3;5;6;4)] -join $delimiter})
# Set the new-ordered column content to the new-file
$newCsv|Set-Content C:\tmp\test2.csv
If you want all of this into one line you can do the following:
Get-Content "c:\tmp\test.csv"|Foreach-object { ($_ -split '|')[#(0..3;5;6;4)] -join '|'}|Set-Content C:\tmp\test2.csv
Note that you cannot use set-content if you have get-content as a part of the pipeline, because the file will be open and cannot replace it's content as a part of the stream.
You'd have to use either the first approach (read the content first, then pass it to the stream) or set the content into a different file.