Cleaner replacement method in powershell - powershell

I have written the following code in powershell and I think there is a better way to replace strings in a given file
$(Get-content $output_file) -replace $regex2,'' | set-content $output_file
$(Get-content $output_file) -replace '_','(' | set-content $output_file
$(Get-content $output_file) -replace '"',')' | set-content $output_file
The first replace is for a regex - which is ok.
However in the second and third instances I'm replacing an "_" and replacing a "quote" with open and close parenthesis respectively.
Is there a cleaner way to write this code?

You can combine them to avoid reading and saving the file multiple times + easier to read (in my opinion). Ex:
(Get-content $output_file) -replace $regex2 -replace '_', '(' -replace '"', ')' | set-content $output_file

Related

How to read every csv file in the folder and remove quote character from the csv file?

How can i read every csv file the specific folder? When script below is executed, it only will remove quote character of one csv file.
$file="C:\test\IV-1-2020-04-02.csv"
(GC $file) | % {$_ -replace '"', ''} > $file
Get-ChildItem -Path C:\test\ -Filter '*.csv'
The output only will remove the quote character of "IV-1-2020-04-02.csv". What if i have different filename ?
You can iterate each .csv file from Get-ChildItem and replace the quotes " with '' using Set-Content.
$files = Get-ChildItem -Path "YOUR_FOLDER_PATH" -Filter *.csv
foreach ($file in $files)
{
Set-Content -Path $file.FullName -Value ((Get-Content -Path $file.FullName -Raw) -replace '"', '')
}
Make sure to pass your folder path to -Path, which tells Get-ChildItem to fetch every file from this folder
Its also faster to use the -Raw switch for Get-Content, since it reads the file into one string and preserves newlines. If you omit this switch, Get-Content will by default split the lines by newlines into an array of strings
If you want to read files in deeper sub directories as well, then add the -Recurse switch to Get-ChildItem:
$files = Get-ChildItem -Path "YOUR_FOLDER_PATH" -Filter *.csv -Recurse
Addtionally, you could also use Foreach-Object here:
Get-ChildItem -Path "YOUR_FOLDER_PATH" -Filter *.csv -Recurse | ForEach-Object {
Set-Content -Path $_.FullName -Value ((Get-Content -Path $_.FullName -Raw) -replace '"', '')
}
Furthermore, you could replace Foreach-Object with its alias %. However, If your using VSCode and have PSScriptAnalyzer enabled, you may get this warning:
'%' is an alias of 'ForEach-Object'. Alias can introduce possible problems and make scripts hard to maintain. Please consider changing alias to its full content.
Which warns against using aliases for maintainability. Its much safer and more portable to use the full version. I only use the aliases for quick command line usage, but when writing scripts I use the full versions.
Note: The above solutions could potentially corrupt the CSV if some lines need quoting. This solution simply goes through the whole file and replaces every quote with ''. PowerShell 7 offers a -UseQuotes AsNeeded option for Export-Csv, so you may look into that instead.
Don't just replace all the " unless you are very certain that it's a good idea; otherwise replace the " when it shouldn't matter because the field doesn't contain text with a comma, double quote, nor line break. (see RFC-4180 section 2, #6 and #7)
As with any script that overwrites its working files, make sure you have backups of those files should you want an undo option later on...
$tog = $true
$sep = ':_:'
$header=#()
filter asString{
$obj=$_
if($tog){
$header=(gm -InputObject $obj -Type NoteProperty).Name
$hc = $header.Count-1
$tog=$false
$str = $header -join $sep
$str = "$sep$str" -replace '"','""'
$str = $str -replace "$sep(((?!$sep)[\s\S])*(,|""|\n)((?!$sep)[\s\S])*)",($sep+'"$1"')
($str -replace $sep,',').Substring(1)
}
$str = (0..$hc | %{$obj.($header[$_])}) -join $sep
$str = "$sep$str" -replace '"','""'
$str = $str -replace "$sep(((?!$sep)[\s\S])*(,|""|\n)((?!$sep)[\s\S])*)",($sep+'"$1"')
($str -replace $sep,',').Substring(1)
}
ls *.csv | %{$tog=$true;import-csv $_ | asString | sc "$_.new";$_.FullName} | %{if(test-path "$_.new"){mv "$_.new" $_ -force}}
Note: the CSV files are expected to contain their own headers. You could work around that if you needed to with the use of the -Header option of Import-Csv

Replacing/inserting newlines using Powershell

(I have read the other threads with similar names...)
I'm new to PowerShell. I am trying to understand how to find and replace newlines. For example, find double newlines and replace them with a single or vice versa.
I have a test document that was created using Notepad:
The quick brown fox jumped over the lazy dog
The quick brown fox jumped over the lazy dog
The quick brown fox jumped over the lazy dog
The quick brown fox jumped over the lazy dog
I am working in the PowerShell ISE for testing/learning.
When I run the following command (attempting to replace one newline with two):
((Get-Content -path $filename -raw) -replace '`n','`n`n') | Set-Content -path $filename
Get-Content -path $filename -raw
The output is unchanged. So I tried the following and it remained unchanged.
((Get-Content -path $filename -raw) -replace '`r`n','`r`n`r`n') | Set-Content -path $filename
So, knowing that PowerShell uses a back-tick rather than a backslash, but out of frustration, I tried the following command:
((Get-Content -path $filename -raw) -replace '\n','\n\n') | Set-Content -path $filename
And, surprisingly (to me), all of the newlines were replaced, but with the string literal '\n\n'. So it seems searching for a newline worked with a backslash but not with a back-tick. The replacement, unfortunately, was the literal string rather than the CRLF I need.
I'm stumped. But for what it's worth, I also tried the following and the string literal was again used for the replacement (i.e., in place of newlines, the document contained '`r`n').
((Get-Content -path $filename -raw) -replace '\n','`r`n') | Set-Content -path $filename
I have seen many posts where people were mistakenly using a backslash, but in my case it seems like a backslash is required for the search, and I don't understand what is required to replace a newline.
Thanks!
'`n' just matches the literal characters [backtick][n], which isn't what you want. You want to interpret those values. For that, you'll need to use double quotes i.e., '`n' should be "`n". According to Microsoft...
The special characters in PowerShell begin with the backtick
character, also known as the grave accent (ASCII 96). ... These
characters are case-sensitive. The escape character is only
interpreted when used within double quoted (") strings.
Use double quotes. You probably also want the -nonewline option to set-content, so that another `r`n doesn't get put at the end of the file.
PS> '`n'
`n
PS> "`n"
PS> (Get-Content -path $filename -raw) -replace "`n","`n`n" |
Set-Content -path $filename -nonewline
There are several ways of doing this. First one is to read the file as a single string and perform a regex -replace on it:
Remember that on Windows machines the Newline is a combination of two characters CR ('\r', ASCII value 13) and LF ('\n', ASCII value 10).
$filename = 'D:\test.txt'
# replace single newlines by a double newline
$replaceWith = '{0}{0}' -f [Environment]::NewLine
(Get-Content -Path $filename -Raw) -replace '\r?\n', $replaceWith | Set-Content -Path 'D:\test-to-double.txt' -Force
# replace double newlines by a single newline
$replaceWith = [Environment]::NewLine
(Get-Content -Path $filename -Raw) -replace '(\r?\n){2}', $replaceWith | Set-Content -Path 'D:\test-to-single.txt' -Force
Another way is to read in the file as string array (let PowerShell deal with single newlines):
# read the file as string array and join the elements with a double newline
$replaceWith = '{0}{0}' -f [Environment]::NewLine
(Get-Content -Path $filename) -join $replaceWith | Set-Content -Path 'D:\test-to-double.txt' -Force
# read the file as string array and join the elements with a single newline
$replaceWith = [Environment]::NewLine
(Get-Content -Path $filename) -join $replaceWith | Set-Content -Path 'D:\test-to-single.txt' -Force
The latter method is also extremely suited for removing empty or whitespace-only lines before you 'normalize' the newlines in the text:
In that case, just replace (Get-Content -Path $filename) with (Get-Content -Path $filename | Where-Object { $_ -match '\S' })

Why does this code only output lines that I made a replacement on?

I wrote this code to test out text manipulation. For each line read from my text file I replace tabs/returns/spaces, then I check if the line contains the carachters 'SAAS' and remove an A. I then write the content to an new file.
The issue is that the new file contains only lines that I made a replacement on and deletes any other lines of text from the orinal file when writing to the new file.
$Text = Get-Content -Path C:\Desktop\Phones\Phones_1.txt |
ForEach-Object {($_ -replace '\n','')} |
ForEach-Object {($_ -replace '\r','')} |
ForEach-Object {($_ -replace '\s','')} |
ForEach-Object {IF($_ | Select-String -Pattern 'SAAS'){$_ -replace 'SAAS','SAS'}}
$Text | Out-File 'C:\Desktop\Phones\phone2.txt'
Any help is appriciated.
It probably has to do with your Select-String function, which I don't believe needs to be there. Also, you can chain together replace statements greatly reducing the need to keep piping your code. I don't think it causes any issues, but you don't need to wrap your ForEach-Object blocks in braces either. Here's what that would look like:
$Text = Get-Content -Path C:\Desktop\Phones\Phones_1.txt |
ForEach-Object { $_ -replace '\n','' -replace '\r','' -replace '\s','' -replace 'SAAS','SAS' }
$Text | Out-File 'C:\Desktop\Phones\phone2.txt'
As $Text contains an array of single lines, replacing \r,\n is pretty useless.
You should provide an example of your input and expected output by editing your question.
Using a RegularExpression with lookarounds
(Get-Content .\Phones_1.txt -raw) -replace '\r|\n|\s|(?<=SA)A(?=S)'|Set-Content Phone2.txt
Yields this output from your above questions complete text:
Iwrotethiscodetotestouttextmanipulation.ForeachlinereadfrommytextfileIreplacetabs/returns/spaces,thenIcheckifthelinecontainsthecarachters'SAS'andremoveanA.Ithenwritethecontenttoannewfile.TheissueisthatthenewfilecontainsonlylinesthatImadeareplacementonanddeletesanyotherlinesoftextfromtheorinalfilewhenwritingtothenewfile.$Text=Get-Content-PathC:\Desktop\Phones\Phones_1.txt|ForEach-Object{($-replace'\n','')}|ForEach-Object{($-replace'\r','')}|ForEach-Object{($-replace'\s','')}|ForEach-Object{IF($|Select-String-Pattern'SAS'){$_-replace'SAS','SAS'}}$Text|Out-File'C:\Desktop\Phones\phone2.txt'Anyhelpisappriciated.

Replace new lines in CLXML file

After generating a CLXML file:
[string]$myString = 'foobar' | Export-Clixml -Path C:\Files\test.clxml
I'm trying to remove line breaks after the right close anchor >. I have tried:
(Get-Content C:\Files\test.clxml) -replace "`n", "" | Set-Content C:\Files\test.clxml
Also tried using -replace r but this strips out r characters from the file.
What am I doing wrong?
Get-Content returns an array holding each single line (not containing any line feeds).
Set-Content writes your array of lines to a single file separating them with line feeds.
Meaning you should do the following to get what you want:
(Get-Content C:\Files\test.clxml) -join "" | Set-Content C:\Files\test.clxml
Your issue is that in your test there are no newlines to replace. Get-Content is returning a string array that is seen as having newlines on screen when rendered. To actually get them inside the string to be manipulated try one of these.
(Get-Content C:\Files\test.clxml -Raw) -replace "`n" | Set-Content C:\Files\test.clxml
(Get-Content C:\Files\test.clxml | Out-String) -replace "`n" | Set-Content C:\Files\test.clxml
The latter would be needed if your have PS Version 2.0

Search for >$(bla)< and replace

I must replace a value in a file. This works for normal text with this command
(Get-Content $file) | Foreach-Object {$_ -replace "SEARCH", "REPLACE"} | Set-Content $file
But now, the search text is "$(SEARCH)" (without quotes). Backslash escaping the '$' with '`$' doesn't work:
(Get-Content $file) | Foreach-Object {$_ -replace "`$(SEARCH)", "BLA"} | Set-Content $file
Any ideas? Thank you.
The -replace operator is actually a regular expression replacement not a simple string replacement, so you've got to escape the regular expression:
(Get-Content $file) | Foreach-Object {$_ -replace '\$\(SEARCH\)', "BLA"} | Set-Content $file
Note that you can suppress string interpolation by using single quotes (') of double quotes (") around string literals, which I've done above.
Macintron,
You can try something like below :
(Get-Content $file) | Foreach-Object {$_.replace('$(SEARCH)', "BLA"} | Set-Content $file
slightly (or even more) faster
sc $file ((gc $file -Raw) -replace '\$\(search\)','BLAHH')