Home > Blockchain >  Powershell Replace - Special characters
Powershell Replace - Special characters

Time:08-20

I need to replace

location ~* "^/(888888-localhost\/client)(.*)$" {
            proxy_pass https://localhost:32583;

by

location ~* "^/(888888-localhost\/client)(.*)$" {
            proxy_pass https://localhost:$proxy;

I'm trying to achieve this with the following code with no success

Get-ChildItem 'C:\nginx\conf\nginx.conf' | ForEach {
     (Get-Content $_) | ForEach  {$_ -Replace "location ~\* \""^/\(888888-localhost\\/client\)(.*)$"" {
            proxy_pass https://localhost:. ;", "location ~* ""^/(888888-localhost\/client)(.*)$" {
                proxy_pass https://localhost:$proxy;""} | Set-Content $_
}

How can i do this using Powershell?

CodePudding user response:

One solution would be to use a positive lookbehind to ensure the pattern is matched:

@"
location ~* "^/(888888-localhost\/client)(.*)$" {
            proxy_pass https://localhost:32583;
"@  -replace "(?<=\/localhost:)\d ",'$port'

# In your use-case
(Get-Content -Path ($Path = 'C:\nginx\conf\nginx.conf')) -replace "(?<=\/localhost:)\d ",'$port' | 
    Set-Content -Path $Path

Removing the single quotes around $port, or replacing them with double quotes will allow the use of $port as an actual variable.

CodePudding user response:

  • Since you want to match across lines, you cannot use the line-by-line processing that Get-Content does by default. Use the -Raw switch to read the entire file at once, into a single, multi-line string.

  • Since both your search string and the replacement string passed to the regex-based -replace operator contain metacharacters, you must escape them.

    • Note: If all you need is literal (verbatim) substring matching, including not needing to assert where the substring must match (start of the string, word boundary, ...), using [string]::Replace() is the simpler option, however:

      • In Windows PowerShell [string]::Replace() is invariably, case-sensitive, in PowerShell (Core) 7 it is by default, unlike PowerShell's operators - see this answer for guidance on when to use -replace vs. [string]::Replace()
    • The search string must be escaped with [regex]::Escape(), so that the .NET regex engine, which the -replace operator invariably uses, treats it as a literal.

    • The substitution string must have any embedded $ characters escaped as $$, so that $ isn't mistaken for the start of a capture-group reference.

    • For a comprehensive overview of how -replace works, see this answer.

# Escape the search string.
$reSearch = [regex]::Escape(@'
location ~* "^/(888888-localhost\/client)(.*)$" {
            proxy_pass https://localhost:32583;
@')

# Escape the substitution string.
$substitution = @"
location ~* "^/(888888-localhost\/client)(.*)$" {
            proxy_pass https://localhost:$proxy;
"@.Replace('$', '$$')

# ...
  ($_ | Get-Content -Raw) -replace $reSearch, $substitution |
    Set-Content -LiteralPath $_.FullName
# ...

Note the use of both verbatim and expandable here-strings to facilitate declaring the strings.

As for a potential simplification of your -replace operation:

  • Abraham Zinala points out that ultimately you're looking to only replace the port number in the matched substring, so you could use a positive look-behind assertion ((?<=...)), as shown in this simplified example:

    $proxy = 8080
    'a https://localhost:80; z' -replace '(?<=https://localhost:)\d ', $proxy
    
    • Output is: a https://localhost:8080; z

Caveat re newlines:

  • The above assumes that your script file uses the same newline format (Windows-format CRLF vs. Unix-format LF) as the input file.

  • If you're not sure, and you want to match either format, replace the \n escape sequence in $reSearch with \r?\n ($reSearch = $reSearch.Replace('\n', '\r?\n') and possibly replace the literal newline in $substitution with `r`n or `n, as needed.

  • Related