37

I am trying to read replace a line in a configuration file using PowerShell. Sometimes this script works, but most of the time it does not replace the line.

(Get-Content D:\home\App_Config\Sitecore.config) `
 | %{ $_ -replace ' <setting name="Media.MediaLinkServerUrl" value=" "/>',' <setting name="Media.MediaLinkServerUrl" value="https://newurl.com"/>'} `
 | Set-Content D:\home\App_Config\Sitecore.config
Anthony Mastrean
22.5k24 gold badges118 silver badges190 bronze badges
asked Nov 18, 2016 at 14:11

4 Answers 4

63

Try the following:

$file = 'D:\home\App_Config\Sitecore.config'
$regex = '(?<=<setting name="Media\.MediaLinkServerUrl" value=")[^"]*'
# Update the target file in-place - be sure to make a backup first.
(Get-Content -Raw $file) -replace $regex, 'https://newurl.com' | 
 Set-Content -NoNewLine $file

Notes on the file-reading and -writing aspect of the solution:

  • Re Get-Content: Use of the -Raw switch (v3+)[1] reads the entire file in full into memory, as a single, multiline string, which greatly speeds up the operation and would be a necessity if you wanted to match across multiple lines.Thanks, deadlydog.

    • By contrast, omit -Raw for (much slower) line-by-line processing; complementarily, omit -NoNewLine from the Set-Content call.

    • Either way, note the required parentheses ((...), the grouping operator) around the Get-Content call to ensure that the pipeline can write back to the same file that Get-Content has read from (in the context of applying an operator such as -replace to Get-Content output, you need the parentheses anyway).

  • Re Set-Content:

    • In Windows PowerShell it uses your system's legacy ANSI encoding by default (typically a fixed, single-byte character encoding such as Windows-1252), so you may want to use -Encoding to control the output file's encoding explicitly.
      PowerShell (Core) 7 fortunately defaults to BOM-less UTF-8, across all cmdlets.
      Similarly, Windows PowerShell's Get-Content interprets a BOM-less input file as ANSI-encoded too, so an -Encoding argument may be needed there as well.
      Notably, Get-Content does not preserve the input file's character encoding, and Set-Content invariably applies its default on writing; see this answer for details.

    • -NoNewLine,(v5+)[1] in combination with Get-Content's -Raw, writes the multiline input string as-is (back) to the target file, without appending a (trailing) newline.

Due to using a regex (regular expression) to match your existing setting in the context of the -replace operator (explained in detail below), any text currently inside value="..." is matched, so this command will work even when run repeatedly, with different replacement URLs, if needed (and repeated runs with the same replacement URL are, in effect, benign no-ops).

By contrast, what you tried uses an effectively literal string (... value=" ") to find what to replace, and after the 1st - potentially successful - run, that literal no longer matches, so subsequent runs do not perform the intended replacement.

The command above uses a streamlined approach to replacement:

  • (?<=<setting name="Media.MediaLinkServerUrl" value=") is a lookbehind assertion ((?<=...)) that matches, but doesn't capture what it matches: it finds the part up to and including the opening " of the value you're trying to replaces, without making that prefix a part of what will get replaced.

  • [^"]* then matches the entire value, up to, but not including the closing ". ([^"] is a character set that matches any character other than (^) a ", and * finds any (possibly empty) sequence of such characters.

  • Therefore, because the regex captured only the value itself, all you need to specify as the replacement string is the new value.


[1] These version numbers refer to - now long obsolete - versions of Windows PowerShell (the legacy, ships-with-Windows, Windows-only edition of PowerShell whose latest and final version is 5.1). The features in question are equally available in (all versions of) PowerShell (Core) 7, the modern, cross-platform, install-on-demand edition of PowerShell.

answered Nov 18, 2016 at 14:42
Sign up to request clarification or add additional context in comments.

Comments

28

Use the Replace method like this:

$file = 'D:\home\App_Config\Sitecore.config'
$find = ' <setting name="Media.MediaLinkServerUrl" value=" "/>'
$replace = ' <setting name="Media.MediaLinkServerUrl" value="https://newurl.com"/>'
(Get-Content $file).replace($find, $replace) | Set-Content $file
answered Nov 18, 2016 at 14:18

6 Comments

.Replace() is a method of .NET type [string], not an operator. Unlike PowerShell's -replace operator, which uses regular expressions, .Replace() performs literal substring replacements, which I suspect may be neha's problem: after an initial (successful) run of the script, the literal doesn't match anymore.
To put it differently: Even though your code is more concise and more efficient, it is in essence the same as the OP's and doesn't explain the intermittent failures. (The only - negligible - functional difference is that the . in the search string in the OP's command, due to use of -replace, is the match-any-character regex metacharacter, whereas it is a literal in your command, as it should be.)
code provide by me some time works perfectlly but sometime even though search string is : <setting name="Media.MediaLinkServerUrl" value=" "/> still it is not replacing it. I will try your script but confused why mine is not working !
@mklement0 's answer is better than mine, he knows the dark art of regex. I missed that class in wizarding school ;)
@mklement no this is not the case...I am manually changing value ="newurl" to value=" " everytime before running script. But still facing the issue..Anyway will work with the script given by you. Thanks :)
|
1

This Function worked for me

I am trying to replace anything come after Infile enter image description here

Function:

function Write-FileContent {
 [cmdletBinding()]
 param(
 [parameter(Mandatory = $true)]
 [string]$FileToReplacePath,
 [parameter(Mandatory = $true)]
 [string]$TextToReplaceWith,
 [parameter(Mandatory = $true)]
 [string]$LineNumber,
 [parameter(Mandatory = $true)]
 [string]$TextToBeingWith 
 )
 $Read = Get-Content -Path $FileToReplacePath
 $Read | ForEach-Object { if ($_.ReadCount -eq $LineNumber) { $_ -replace "'$TextToBeginWith'=.+'", "$TextToReplaceWith" } else { $_ } } | Set-Content $FileToReplacePath
 }

Testing Parameter

$CsvFilePath="C:\msydatfgdfa.csv"
Write-FileContent -FileToReplacePath D:\test.txt -TextToReplaceWith "'$CsvFilePath'" -LineNumber 2 -TextToBeingWith "Infile"
answered Nov 17, 2019 at 10:24

Comments

0

Here's an example that preserves the PSPath property, so you don't have to specify the path to set-content:

(Get-Content -raw input) | ForEach-Object {
 $_ -replace 111,222 |
 Add-Member NoteProperty PSPath $_.PSPath -PassThru
} | Set-Content -nonewline
answered Nov 17, 2019 at 14:30

Comments

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.