Escaping dollar signs in PowerShell path is not working

Powershell

Powershell Problem Overview


Why doesn't this work?


$drvrInstFilePath = "$sharePath$imageName\ISO`$OEM$`$1\RPKTools\RPKDriverInst.bat"
echo $drvrInstFilePath
$drvrInstContent = Get-Content -LiteralPath "$sharePath$imageName\ISO`$OEM$`$1\RPKTools\RPKDriverInst.bat"  | Out-String

The echo shows the right path, but the Get-Content command expands the $oem and $1 to blank strings, even though they are escaped. Why?

Powershell Solutions


Solution 1 - Powershell

Instead of messing around with escaping dollar signs, use single quotes ' instead of double quotes ". It prevents PowerShell expanding $ into a variable. Like so,

$p = "C:\temp\Share\ISO$OEM$"
# Output
C:\temp\Share\ISO$


$p = 'C:\temp\Share\ISO$OEM$'
# Output
C:\temp\Share\ISO$OEM$

If you need to create a path by using variables, consider using Join-Path. Like so,

$s = "Share"
join-path "C:\temp\$s" '\ISO$OEM$' 
# Output
C:\temp\Share\ISO$OEM$

Solution 2 - Powershell

You can actually just use a tick mark to escape the $ like so:

`$

Example:

$number = 5
Write-Host "`$${number}"
# Output: $5

Solution 3 - Powershell

You used double quotes with a single backtick. This is an incorrect combination. In fact, I am not sure that a single backtick alone is sufficient in any case. Your successful options to escape the dollar sign ($) in PowerShell are to use double quotes with a backslash-backtick combination ("\`$find"), or instead to use single quotes with a simple backslash ('$find'). [However, note the exception at the end about function call parameters.] See below for examples.

Also, for those unfamiliar with the distinction, it is important not to confuse the backtick character (`) with the single-quotation character (') in these escapes.

[SUCCESS] Double quotes as container with backslash-backtick as escape:

PS C:\Temp> 'What is $old?' | ForEach-Object {$_ -replace "\`$old", "(New)"}
What is (New)?
PS C:\Temp>

[FAIL] Double quotes as container with backslash-apostrophe as escape:

PS C:\Temp> 'What is $old?' | ForEach-Object {$_ -replace "\'$old", "(New)"}
What is $old?
PS C:\Temp>

[SUCCESS] Single quotes as container with simple backslash as escape:

PS C:\Temp> 'What is $old?' | ForEach-Object {$_ -replace '\$old', "(New)"}
What is (New)?
PS C:\Temp>

[FAIL] Single quotes as container with backslash-backtick as escape:

PS C:\Temp> 'What is $old?' | ForEach-Object {$_ -replace '\`$old', "(New)"}
What is $old?
PS C:\Temp>

Overall, the easiest option may be to use single quotes as the container and a single backslash as the escape: '\$old'

Update: As if the above were not confusing enough, when you use a function call instead of a command, single quotes are needed without an escape. Trying to use an escape on the function call parameter will not work:

[FAIL] Using an escape in a function parameter:

PS C:\Temp> 'What is $old?' | ForEach-Object {$_.ToString().Replace('\$old', "(New)");}
What is $old?
PS C:\Temp>

[SUCCESS] Using plain single quotes without an escape in a function parameter:

PS C:\Temp> 'What is $old?' | ForEach-Object {$_.ToString().Replace('$old', "(New)");}
What is (New)?
PS C:\Temp>

Solution 4 - Powershell

In my case I needed to escape some $'s used in a string but not others that are variables.

For example, my SSRS instance name has a $ sign:

> [ReportServer$SSRS]

To escape the $ sign I use single quotes. Otherwise I use the -join statement to concatenate variables with the strings containing actual $ signs.

 $sql = -join('ALTER DATABASE [ReportServer$', $instanceName,'TempDB]
  SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
  GO
  
  USE [master]
  RESTORE DATABASE [ReportServer$', $instanceName,'TempDB] FROM  DISK = N''C:\temp\ReportServerTempDB.BAK'' WITH  FILE = 1,
    MOVE N''ReportServerTempDB'' TO N''', $sqlDataDrive + "\data_" + $instanceName, '\ReportServer$', $instanceName,'TempDB.mdf'', 
    MOVE N''ReportServerTempDB_log'' TO N''', $sqlLogDrive + "\log_" + $instanceName, '\ReportServer$', $instanceName,'_TempDBlog.LDF'',  NOUNLOAD,  REPLACE, RECOVERY,  STATS = 5
  GO
  
  ALTER DATABASE [ReportServer$', $instanceName,'TempDB]
  SET MULTI_USER;
  GO
  ')

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionBelacView Question on Stackoverflow
Solution 1 - PowershellvonPryzView Answer on Stackoverflow
Solution 2 - Powershellbarak m.View Answer on Stackoverflow
Solution 3 - PowershellMichaelView Answer on Stackoverflow
Solution 4 - PowershellJeremy ThompsonView Answer on Stackoverflow