I am trying to use PowerShell do a simple find and replace. I use template text files and use $ in front of values that need to be changed.
Example:
(Get-Content "D:\test") | Foreach-Object {$_ -replace "`$TBQUAL", "DBO"} | Set-Content "D:\test"
It should find the line OWNER=$TBQUAL and make it look like OWNER=DBO.
I am using the escape in front of $TBQUAL with no luck. To test that it is working if I removed the $ from the front, it would replace TBQUAL and made it look like OWNER=$DBO.
Two things to get this to work:
Use single quotes for your strings so that the $ is not interpreted as the start of a variable to be expanded.
Escape the $ using a backslash "\" so the regular expression parser takes it literally.
For example,
PS C:\> 'Has a $sign in it' -replace 'a \$sign', 'no dollar sign'
Has no dollar sign in it
If you aren't using regular expressions in your replacement, you can do a simple (and fast) replace like this:
Foreach-Object {$_.Replace('$TBQUAL', 'DBO')}
Related
I am writing a script which at one point has to check in a text file and remove certain strings. So far I have this:
powershell -Command "(gc myFile.txt) -replace 'foo', 'bar' | Out-File -encoding ASCII myFile.txt"
The only problem is that that can find and replace but will not remove the line all together.
The second problem is that say I am removing the line that has Mark, it needs to not remove a line that has something like Markus.
I don't know if this is possible with the powershell interface?
Your current code will only replace foo with bar, this is what replace does.
Removing the whole line if it matches requires a different approach, almost backwards, as you can use notmatch to output any lines that do not match you filter - effectively removing them.
Also using regex word boundaries will then only match Mark but not Markus:
(Get-Content file.txt) | Where-Object {$_ -notmatch "\bMark\b"} | Set-Content file.txt
I have a script that I need to replace a couple of lines in. The first replace is going fine but the second is wiping out my file and duplicating the line multiple times.
My code
(get-content $($sr)) -replace 'remoteapplicationname:s:SHAREDAPP',"remoteapplicationcmdline:s:$($sa)" | Out-File $($sr)
(get-content $($sr)) -replace 'remoteapplicationprogram:s:||SHAREDAPP',"remoteapplicationprogram:s:||$($sa)" | Out-File $($sr)
The first replace works perfectly. The second one is causing this:
remoteapplicationprogram:s:||stagaredrremoteapplicationprogram:s:||stagarederemoteapplicationprogram:s:||stagareddremoteapplicationprogram:s:||stagarediremoteapplicationprogram:s:||stagaredrremoteapplicationprogram:s:||stagarederemoteapplicationprogram:s:||stagaredcremoteapplicationprogram:s:||stagaredtremoteapplicationprogram:s:||stagaredcremoteapplicationprogram:s:||stagaredlremoteapplicationprogram:s:||stagarediremoteapplicationprogram:s:||stagaredpremoteapplicationprogram:s:||stagaredbremoteapplicationprogram:s:||stagaredoremoteapplicationprogram:s:||stagaredaremoteapplicationprogram:s:||stagaredrremoteapplicationprogram:s:||stagareddremoteapplicationprogram:s:||stagared:remoteapplicationprogram:s:||stagarediremoteapplicationprogram:s:||stagared:remoteapplicationprogram:s:||stagared1remoteapplicationprogram:s:||stagared
etc...
Is this because of the ||? If so, how do I get around it?
Thanks!
To begin with, you should be using slightly more meaningful names for your variables. Especially if you want someone else to be reviewing your code.
The gist of your issue is that -replace supports regexes (regular expressions), and you have regex control characters in your pattern string. Consider the following simple example, and notice everywhere the replacement string is found:
PS C:\Users\Matt> "ABCD" -replace "||", "bagel"
bagelAbagelBbagelCbagelDbagel
-replace is also an array operator, so it works on every line of the input file, which is nice. For simplicity's sake, if you are not using a regex, you should just consider using the string method .Replace(), but it is case-sensitive, so that might not be ideal. So let's escape those control characters in the easiest way possible:
$patternOne = [regex]::Escape('remoteapplicationname:s:SHAREDAPP')
$patternTwo = [regex]::Escape('remoteapplicationprogram:s:||SHAREDAPP')
(get-content $sr) -replace $patternOne, "remoteapplicationcmdline:s:$sa" | Out-File $($sr)
(get-content $sr) -replace $patternTwo, "remoteapplicationprogram:s:||$sa" | Out-File $($sr)
Now we get both patterns matched as you have them written. Run $patternTwo on the console to see what has changed to it! $patternOne, as written, has no regex control characters in it, but it does not hurt to use the escape method if you are just expecting simple matching.
Aside from the main issue pointed out, there is also some redundancy and misconception that can be addressed here. I presume you are updating a source file to replace all occurrences of those strings, yes? Well, you don't need to read the file in twice, given that you can chain -replace:
$patternOne = [regex]::Escape('remoteapplicationname:s:SHAREDAPP')
$patternTwo = [regex]::Escape('remoteapplicationprogram:s:||SHAREDAPP')
(get-content $sr) -replace $patternOne, "remoteapplicationcmdline:s:$sa" -replace $patternTwo, "remoteapplicationprogram:s:||$sa" |
Set-Content $sr
Perhaps that will do what you intended.
You might notice that I've removed the subexpressions operators ($(...)) around your variables. While they have their place, they don't need to be used here. They are only needed inside more complicated strings, like when you need to expand object properties or something.
I am trying to automate Active Directory installation on Windows Server 2008 using windows powershell. I created a text file with .tmpl extension and added:
[DCINSTALL]
ReplicaOrNewDomain=_ReplicaOrNewDomain__
Then I created an answer file in a text format:
[DCINSTALL]
ReplicaOrNewDomain=$env:ReplicaOrNewDomain
Now I want to be able to write a script in PowerShell which will use the template file to get the value of variable ReplicaOrNewDomain from environment and replace $env:ReplicaOrNewDomain by that value in the text file so that I can use that answer file for AD installation.
You have a few options to do this. One is Environment.ExpandEnvironmentVariables. This uses a %variable% syntax (instead of $env:variable), so it would be simpler if you only want to substitute environment variables:
gc input.tmpl | foreach { [Environment]::ExpandEnvironmentVariables($_) } | sc out.ini
A more complete expansion of PowerShell expressions can be achieve via ExpandString. This is more useful if you want to insert actual PowerShell expressions into the template:
gc input.tmpl | foreach { $ExecutionContext.InvokeCommand.ExpandString($_) } | sc out.ini
A third option would be something like a customized templating scheme that uses Invoke-Expression, which I implemented here.
You can do that with a simple replacement like this:
$f = 'C:\path\to\your.txt'
(Get-Content $f -Raw) -replace '\$env:ReplicaOrNewDomain', $env:ReplicaOrNewDomain |
Set-Content $f
or like this:
$f = 'C:\path\to\your.txt'
(Get-Content $f -Raw).Replace('$env:ReplicaOrNewDomain', $env:ReplicaOrNewDomain) |
Set-Content $f
Note that when using the -replace operator you need to escape the $ (because otherwise it'd have the special meaning "end of string"). When using the Replace() method you just need to use single quotes to prevent expansion of the variable in the search string.
However, why the intermediate step of replacing the template parameter _ReplicaOrNewDomain__ with a different template parameter $env:ReplicaOrNewDomain? You would make your life easier if you just kept the former and replaced that with the value of the environment variable ReplicaOrNewDomain.
One thing that I like to do with my template files is something like this.
[DCINSTALL]
ReplicaOrNewDomain={0}
OtherVariable={1}
Then in my code I can use the format operator -f to make the changes.
$pathtofile = "C:\temp\test.txt"
(Get-Content $pathtofile -Raw) -f $env:ReplicaOrNewDomain, "FooBar" | Set-Content $pathtofile
It can help if you have multiple things that you need to update at once. Update your file with as many place holders as you need. You can use the same one multiple times if need be in the file.
[DCINSTALL]
ReplicaOrNewDomain={0}
SimilarVariable={0}
Caveat
If your actual file is supposed to contain curly braces you need to double them up to the are escaped.
You can use the ExpandString function, like this:
$ExecutionContext.InvokeCommand.ExpandString($TemplVal)
(assuming $TemplVal has the template string).
I am trying to create a Powershell script which wraps quotes around each columns of the file on export to CSV. However the Export-CSV applet only places these where they are needed, i.e. where the text has a space or similar within it.
I have tried to use the following to wrap the quotes on each line but it ends up wrapping three quotes on each column.
$r.SURNAME = '"'+$r.SURNAME+'"';
Is anyone able to share how to forces these on each column of the file - so far I can just find info on stripping these out.
Thanks
Perhaps a better approach would be to simply convert to CSV (not export) and then a simple regex expression could add the quotes then pipe it out to file.
Assuming you are exporting the whole object $r:
$r | ConvertTo-Csv -NoTypeInformation `
| % { $_ -replace ',(.*?),',',"$1",' } `
| Select -Skip 1 | Set-Content C:\temp\file.csv
The Select -Skip 1 removes the header. If you want the header just take it out.
To clarify what the regex expression is doing:
Match: ,(.*?),
Explanation: This will match section of each line that has a comma followed by any number of characters (.*) without being greedy (? : basically means it will only match the minimum number of characters that is needed to complete the match) and the finally is ended with a comma. The parenthesis will hold everything between the two commas in a match variable to be used later in the replace.
Replace: ,"$1",
Explanation: The $1 holds the match between the two parenthesis mention above in the match. I am surrounding it with quotes and re-adding the commas since I matched on those as well they must be replaced or they are simply consumed. Please note, that while the match portion of the -replace can have double quotes without an issue, the replace section must be surrounded in single quotes or the $1 gets interpreted by PowerShell as a PowerShell variable and not a match variable.
You can also use the following code:
$r.SURNAME = "`"$($r.SURNAME)`""
I have cheated to get what I want by re-parsing the file through the following - guess that it acts as a simple find and replace on the file.
get-content C:\Data\Downloads\file2.csv
| foreach-object { $_ -replace '"""' ,'"'}
| set-content C:\Data\Downloads\file3.csv
Thanks for the help on this.
I have a series of files that contain a string of characters such as this
(AEDOGRES)
An example file name would be
"Test 1(DOWKFUET).png"
I want to use a powershell command to get rid of the entire string in the parentheses to get something like this.
"Test 1.png"
I would expect to be able to use a command like this
dir | rename-item –NewName { $_.name –replace “(*)“,”” }
However, this is not working for me. Any help is greatly appreciated!
I am running powershell 3.0
Regex and wildcard matching have different rules. In regex, the * means "match zero or more of the previous character", and the parens are reserved characters used for grouping constructs and have to be escaped (using a backslash) if you want them to be used as a literal match.
See Get-Help about_regular_expressions, and try this:
$name = "Test 1(DOWKFUET).png"
$name -replace '\(.+\)',''