Problems with replacing newline - powershell

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 )

Related

Powershell - Count number of carriage returns line feed in .txt file

I have a large text file (output from SQL db) and I need to determine the row count. However, since the source SQL data itself contains carriage returns \r and line feeds \n (NEVER appearing together), the data for some rows spans multiple lines in the output .txt file. The Powershell I'm using below gives me the file line count which is greater than the actual SQL row count. So I need to modify the script to ignore the additional lines - one way of doing it might be just counting the number of times CRLF or \r\n occurs (TOGETHER) in the file and that should be the actual number of rows but I'm not sure how to do it.
Get-ChildItem "." |% {$n = $_; $c = 0; Get-Content -Path $_ -ReadCount 1000 |% { $c += $_.Count }; "$n; $c"} > row_count.txt
I just learned myself that the Get-Content splits and streams each lines in a file by CR, CRLF, and LF sothat it can read data between operating systems interchangeably:
"1`r2`n3`r`n4" | Out-File .\Test.txt
(Get-Content .\Test.txt).Count
4
Reading the question again, I might have misunderstood your question.
In any case, if you want to split (count) on only a specific character combination:
CR
((Get-Content -Raw .\Test.txt).Trim() -Split '\r').Count
3
LF
((Get-Content -Raw .\Test.txt).Trim() -Split '\n').Count
3
CRLF
((Get-Content -Raw .\Test.txt).Trim() -Split '\r\n').Count # or: -Split [Environment]::NewLine
2
Note .Trim() method which removes the extra newline (white spaces) at the end of the file added by the Get-Content -Raw parameter.
Addendum
(Update based on the comment on the memory exception)
I am afraid that there is currently no other option then building your own StreamReader using the ReadBlock method and specifically split lines on a CRLF. I have opened a feature request for this issue: -NewLine Parameter to customize line separator for Get-Content
Get-Lines
A possible way to workaround the memory exception errors:
function Get-Lines {
[CmdletBinding()][OutputType([string])] param(
[Parameter(ValueFromPipeLine = $True)][string] $Filename,
[String] $NewLine = [Environment]::NewLine
)
Begin {
[Char[]] $Buffer = new-object Char[] 10
$Reader = New-Object -TypeName System.IO.StreamReader -ArgumentList (Get-Item($Filename))
$Rest = '' # Note that a multiple character newline (as CRLF) could be split at the end of the buffer
}
Process {
While ($True) {
$Length = $Reader.ReadBlock($Buffer, 0, $Buffer.Length)
if (!$length) { Break }
$Split = ($Rest + [string]::new($Buffer[0..($Length - 1)])) -Split $NewLine
If ($Split.Count -gt 1) { $Split[0..($Split.Count - 2)] }
$Rest = $Split[-1]
}
}
End {
$Rest
}
}
Usage
To prevent the memory exceptions it is important that you do not assign the results to a variable or use brackets as this will stall the PowerShell PowerShell pipeline and store everything in memory.
$Count = 0
Get-Lines .\Test.txt | ForEach-Object { $Count++ }
$Count
The System.IO.StreamReader.ReadBlock solution that reads the file in fixed-size blocks and performs custom splitting into lines in iRon's helpful answer is the best choice, because it both avoids out-of-memory problems and performs well (by PowerShell standards).
If performance in terms of execution speed isn't paramount, you can take advantage of
Get-Content's -Delimiter parameter, which accepts a custom string to split the file content by:
# Outputs the count of CRLF-terminated lines.
(Get-Content largeFile.txt -Delimiter "`r`n" | Measure-Object).Count
Note that -Delimiter employs optional-terminator logic when splitting: that is, if the file content ends in the given delimiter string, no extra, empty element is reported at the end.
This is consistent with the default behavior, where a trailing newline in a file is considered an optional terminator that does not resulting in an additional, empty line getting reported.
However, in case a -Delimiter string that is unrelated to newline characters is used, a trailing newline is considered a final "line" (element).
A quick example:
# Create a test file without a trailing newline.
# Note the CR-only newline (`r) after 'line 1'
"line1`rrest of line1`r`nline2" | Set-Content -NoNewLine test1.txt
# Create another test file with the same content plus
# a trailing CRLF newline.
"line1`rrest of line1`r`nline2`r`n" | Set-Content -NoNewLine test2.txt
'test1.txt', 'test2.txt' | ForEach-Object {
"--- $_"
# Split by CRLF only and enclose the resulting lines in [...]
Get-Content $_ -Delimiter "`r`n" |
ForEach-Object { "[{0}]" -f ($_ -replace "`r", '`r') }
}
This yields:
--- test1.txt
[line1`rrest of line1]
[line2]
--- test2.txt
[line1`rrest of line1]
[line2]
As you can see, the two test files were processed identically, because the trailing CRLF newline was considered an optional terminator for the last line.

Change pipe delimited file to comma delimited in 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"

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

String replacement not working in powershell script at runtime

I have powershell file in which i have line of variable decalration as below
[string] $global:myExePath = "\\myshare\code\scripts";
I want to replace \\myshare\code\scripts with \\mynewshare\code1\psscript at runtime by executing a powershell script.
I am using
Get-Content $originalfile | ForEach-Object { $_ -replace "\\myshare\code\scripts", $mynewcodelocation.FullName } | Set-Content ($originalfile)
If am execuing
{ $_ -replace "scripts", $mynewcodelocation.FullName } it is working fine, but it is not working for { $_ -replace "\\myshare\code\scripts", $mynewcodelocation.FullName }
What is wrong here ?
'\' is a special regex character used to escape other special character.You need to double each back slash to match one back slash.
-replace "\\\\myshare\\code\\scripts",$mynewcodelocation.FullName
When you don't know the content of a string you can use the escape method to escape a string for you:
$unc = [regex]::escape("\\myshare\code\scripts")
$unc
\\\\myshare\\code\\scripts
-replace $unc,$mynewcodelocation.FullName

Powershell: Find and replace words split by newline

So I have a text file that looks something like this:
Members : {USER\member1, USER\member2, US
ER\member3, USER\member4, USER
\member5, USER\member6}
and I would like to remove USER\. The following code removes it but not when it's split by a newline, for example when US on one line and ER\ on another line.
Foreach-Object { %{$_.Replace('USER\', '') }
Putting `n or `r in there doesn't work. Any help is appreciated.
Try this:
PS > ((Get-Content .\t.txt) | % { $_.Trim() }) -join "" -replace "USER\\"
Members : {member1, member2, member3, member4, member5, member6}
If the text is in a string-array, switch out (Get-Content .\t.txt) with your variable. If you have the text in a string(not array) variable, use:
($MYSTRINGVAR.Split("`r`n") | % { $_.Trim() }) -join "" -replace "USER\\"
EDIT Just modify the "Members" part:
$text = (Get-Content .\input.txt) -join "`r`n"
($text | Select-String '(?s)(?<=Members : \{)(.+?)(?=\})' -AllMatches).Matches | % {
$text = $text.Replace($_.Value, ($_.Value -split "`r`n" | % { $_.Trim() }) -join "" -replace "USER\\")
}
$text | Set-Content output.txt
There are probably easier ways to get there, but you can give this one a try:
$Text = #'
Members : {USER\member1, USER\member2, US
ER\member3, USER\member4, USER
\member5, USER\member6}
'#
# First - USER\ with newline inside..
foreach ($index in 1..4) {
$Text = $Text -replace ('USER\\'.Insert($index,'(\r\n\s+)')), '$1'
}
# Than - pure USER\
$Text = $Text -replace 'USER\\'
$Text
As you can see I create few patterns that contain and keep that element in results (, '$1'). For simple ones - I just remove USER\
I've used herestring to create text to work with, it's possible that \r may not be needed for actual file.
This is actually just a comment to Graimer's solution, but it would've been too long and also not readable enough as a comment, so I'm making it a (supplementary) answer instead.
To re-wrap the string after removing USER\ you could do something like this:
$s = "Members : {member1, member2, member3, member4, member5, member6}"
$s -match '^(.*?{)(.*)(})$'
$pad = " " * $matches[1].Length
$matches[1] + ($matches[2] -replace '(.{1,20},) ', "`$1`r`n$pad") + $matches[3]
The first regular expression splits the string into 3 parts that can be accessed via the $matches collection:
Name Value
---- -----
3 }
2 member1, member2, member3, member4, member5, member6
1 Members : {
0 Members : {member1, member2, member3, member4, member5, member6}
$matches[1] is the prologue including the opening curly bracket, $matches[2] is the member list, and $matches[3] is the closing curly bracket. Now you only need to wrap $matches[2] at whatever length you want:
'(.*{1,20},) '
The above means "longest match of at most 20 characters followed by a comma and a space". Replace that with the 1st group ($1) followed by a line-break (```rn``) and a number of spaces that matches the length of the prologue ($pad`) and glue it back together with prologue and trailing curly bracket.