Change pipe delimited file to comma delimited in Powershell - powershell

I have a pipe delimited .TXT file. I need to change the delimiter to a comma instead but still keep the file extension as .TXT. The file looks like this:
Column 1 |Column 2
13|2019-09-30
96|2019-09-26
173|2019-09-25
I am using Windows Powershell 5.1 version for my script.
I am using the following code:
$file = New-Object System.IO.StreamReader -Arg "c:\file.txt"
$outstream = [System.IO.StreamWriter] "c:\out.txt"
while ($line = $file.ReadLine()) {
$s = $line -replace '|', ','
$outstream.WriteLine($s)
}
$file.close()
$outstream.close()
Instead of just replacing the pipe with a comma, the output file looks like this:
C,o,l,u,m,n, 1 , |,C,o,l,u,m,n, 2
1,3,|,2,0,1,9,-,0,9,-,3,0
9,6,|2,0,1,9,-,0,9,-,2,6
1,7,3,|,2,0,1,9,-,0,9,-,2,5

The only problem with your answer is in how you try to replace the | characters in the input:
$s = $line -replace '|', ',' # WRONG
PowerShell's -replace operator expects a regex (regular expression) as its first RHS operand, and | is a regex metacharacter (has special meaning)[1]; to use it as a literal character, you must \-escape it:
# '\'-escape regex metacharacter '|' to treat it literally.
$s = $line -replace '\|', ','
While PowerShell's -replace operator is very flexible, in simple cases such as this one you can alternatively use the [string] type's .Replace() method, which performs literal string replacements and therefore doesn't require escaping (it's also faster than -replace):
# Use literal string replacement.
# Note: .Replace() is case-*sensitive*, unlike -replace
$s = $line.Replace('|', ',')
[1] | denotes an alternation in a regex, meaning that the subexpressions on either side are matched against the input string and one of them matching is sufficient; if your full regex is just |, it effectively matches the empty string before and after each character in the input, which explains your symptom; e.g., 'foo' -replace '|', '#' yields #f#o#o#

You can use Import-Csv and Export-Csv by specifying the -Delimiter.
Import-Csv -Delimiter '|' -Path "c:\file.txt" | Export-Csv -Delimiter ',' -Path "c:\file.txt" -NoTypeInformation

You will find the -split and -join operators to be of interest.
Get-Content -Path "C:\File.TXT" | ForEach-Object { ($_ -split "\|") -join "," } | Set-Content -Path "C:\Out.TXT"

Related

Split string in Powershell

I have sth written in a ".ini" file that i want to read from PS. The file gives the value "notepad.exe" and i want to give the value "notepad" into a variable. So i do the following:
$CLREXE = Get-Content -Path "T:\keeran\Test-kill\test.ini" | Select-String -Pattern 'CLREXE'
#split the value from "CLREXE ="
$CLREXE = $CLREXE -split "="
#everything fine untill here
$CLREXE = $CLREXE[1]
#i am trying to omit ".exe" here. But it doesn't work
$d = $CLREXE -split "." | Select-String -NotMatch 'exe'
How can i do this ?
#Mathias R. Jessen is already answered your question.
But instead of splitting on filename you could use the GetFileNameWithoutExtension method from .NET Path class.
$CLREXE = "notepad.exe"
$fileNameWithoutExtension = [System.IO.Path]::GetFileNameWithoutExtension($CLREXE)
Write-Host $fileNameWithoutExtension # this will print just 'notepad'
-split is a regex operator, and . is a special metacharacter in regex - so you need to escape it:
$CLREXE -split '\.'
A better way would be to use the -replace operator to remove the last . and everything after it:
$CLREXE -replace '\.[^\.]+$'
The regex pattern matches one literal dot (\.), then 1 or more non-dots ([^\.]+) followed by the end of the string $.
If you're not comfortable with regular expressions, you can also use .NET's native string methods for this:
$CLREXE.Remove($CLREXE.LastIndexOf('.'))
Here, we use String.LastIndexOf to locate the index (the position in the string) of the last occurrence of ., then removing anything from there on out

Search and replace a string in PowerShell

I need to search and replace values in a file using the values from another file. For example, A.txt has a string with a value LICENSE_KEY_LOC=test_lic and B.txt contains the string LICENSE_KEY_LOC= or some value in it. Now I need to replace the complete string in B.txt with the value from A.txt. I tried the following but for some reason it does not work.
$filename = "C:\temp\A.txt"
Get-Content $filename | ForEach-Object {
$val = $_
$var = $_.Split("=")[0]
$var1 = Write-Host $var'='
$_ -replace "$var1", "$val"
} | Set-Content C:\temp\B.txt
You may use the following, which assumes LICENSE_KEY_LOC=string is on a line by itself in the file and only exists once:
$filename = Get-Content "c:\temp\A.txt"
$replace = ($filename | Select-String -pattern "(?<=^LICENSE_KEY_LOC=).*$").matches.value
(Get-Content B.txt) -replace "(?<=^LICENSE_KEY_LOC=).*$","$replace" | Set-Content "c:\temp\B.txt"
For updating multiple single keys/fields in a file, you can use an array and loop through each element by updating the $Keys array:
$filename = Get-Content "c:\temp\A.txt"
$Keys = #("LICENSE_KEY_LOC","DB_UName","DB_PASSWD")
ForEach ($Key in $Keys) {
$replace = ($filename | Select-String -pattern "(?<=^$Key=).*$").matches.value
(Get-Content "c:\temp\B.txt") -replace "(?<=^$Key=).*$","$replace" | Set-Content "c:\temp\B.txt"
}
You can put this into a function as well to make it more modular:
Function Update-Fields {
Param(
[Parameter(Mandatory=$true)]
[Alias("S")]
[ValidateScript({Test-Path $_})]
[string]$SourcePath,
[Parameter(Mandatory=$true)]
[Alias("D")]
[ValidateScript({Test-Path $_})]
[string]$DestinationPath,
[Parameter(Mandatory=$true)]
[string[]]$Fields
)
$filename = Get-Content $SourcePath
ForEach ($Key in $Fields) {
$replace = ($filename | Select-String -pattern "(?<=^$Key=).*$").matches.value
(Get-Content $DestinationPath) -replace "(?<=^$Key=).*$","$replace" | Set-Content $DestinationPath
}
}
Update-Fields -S c:\temp\a.txt -D c:\temp\b.txt -Fields "LICENSE_KEY_LOC","DB_UName","DB_PASSWD"
Explanation - Variables and Regex:
$replace contains the result of a string selection that matches a regex pattern. This is a case-insensitive match, but you can make it case-sensitive using -CaseSensitive parameter in the Select-String command.
(?<=^LICENSE_KEY_LOC=): Performs a positive lookbehind regex (non-capturing) of the string LICENSE_KEY_LOC= at the beginning of a line.
(?<=) is a positive lookbehind mechanism of regex
^ marks the beginning of the string on each line
LICENSE_KEY_LOC= is a string literal of the text
.*$: Matches all characters except newline and carriage return until the end of the string on each line
.* matches zero or more characters except newline and carriage return because we did not specify single line mode.
$ marks the end of the string on each line
-replace "(?<=^LICENSE_KEY_LOC=).*$","$replace" is the replace operator that does a regex match (first set of double quotes) and replaces the contents of that match with other strings or part of the regex capture (second set of double quotes).
"$replace" becomes the value of the $replace variable since we used double quotes. If we had used single quotes around the variable, then the replacement string would be literally $replace.
Get-Content "c:\temp\A.txt" gets the contents of the file A.txt. It reads each line as a [string] and stores each line in an [array] object.
Explanation - Function:
Parameters
$SourcePath represents the path to the source file that you want to read. I added alias S so that -S switch could be used when running the command. It validates that the path exists ({Test-Path $_}) before executing any changes to the files.
$DestinationPath represents the path to the source file that you want to read. I added alias D so that -D switch could be used when running the command. It validates that the path exists ({Test-Path $_}) before executing any changes to the files.
$Fields is a string array. You can input a single string or multiple strings in an array format (#("string1","string2") or "string1","string2"). You can create a variable that contains the string array and then just use the variable as the parameter value like -Fields $MyArray.

using matched patterns in powershell -replace

The following prints out 123$replace456, I'd like it to print 123yyy456. How do I do this in powershell?
$path = '123xxx456'
$search = "(\d*)xxx(\d*)"
$replace = 'yyy'
$path -replace $search, '$1$replace$2'
Use a double-quoted string for the replacement pattern in order for $replace to expand correctly. Remember to escape the $ in front of backreferences (ie `$1):
$path -replace $search, "`$1$replace`$2"

replace a string in a csv file with powershell

I need to place System.Object[] for some columns in a csv file. I tried 3 different method but none of them are working. System.Object[] is put in by powershell when that object is empty or something.
$file = "c:\output.csv"
(gc $file) -replace "'system.object[]'", ""
[io.file]::readalltext($file).replace("'system.object[]'","")
(Get-Content $file | ForEach-Object { $_ -replace "system.object[]", "" } ) | Set-Content $file
I added following code to the variable that containing System.Object[] on output. and it's seems to be working. and now I dont have to do the replacement at file level.
"Access Code" = (#($AccessCode) | Out-String).Trim()
The bracers and the dot ([, ], .) need all to be escaped. Furthermore remove the double quotation marks, just keep the single ones. Also think about using creplace, in case you want to work case insensitive. So the command would look like this:
(gc $file) -replace 'system\.object\[\]', ''
In case you want to write everything to a new file:
(gc $file) -replace 'system\.object\[\]', ''|out-file "test2.txt" -encoding ASCII
Just use Escape character
(gc $file) -replace 'system.object\[\]', ""
The characters '[' and ']' are used for Regex pattern. You must use Escape
character '\' to tell Powershell that This is a regular chars

Problems with replacing newline

Iam trying to replace following string with PowerShell:
...
("
Intel(R) Network Connections 14.2.100.0
","
14.2.100.0
")
...
The code that I use is:
Get-Content $logfilepath |
Foreach-Object { $_ -replace '`r`n`r`n', 'xx'} |
Set-Content $logfilepath_new
But I have no success, can someone say me, where the error is?
First, you are using single quotes in the replace string -
'`r`n`r`n'
that means they are treated verbatim and not as newline characters, so you have to use -
"`r`n`r`n"
To replace, read the file as string and use the Replace() method
$content=[string] $template= [System.IO.File]::ReadAllText("test.txt")
$content.Replace("`r`n`r`n","xx")
Get-content returns an array of lines, so CRLF is essentially your delimiter. Two CRLF sequences back to back would be interpreted as the end of the currrent line, followed by a null line, so no line (object) should contain '`r`n`r`n'. A multi-line regex replace would probably be a better choice.
as alternate method using PS cmdlets:
Get-Content $logfilepath |
Foreach-Object -Begin { $content="" } -Process { $content += $_ ; $content += "xx" } -End { $content } |
Set-Content $logfilepath_new
I used the following code to replace somestring with newline:
$nl = [System.Environment]::NewLine
$content = $content.Replace( somestring, $nl )