How to delay variable expansion in PowerShell strings?

Whatever you call it, I'm trying to figure out a way to get the contents of an existing string and evaluate them as a double-quoted string. For example, if I create the following lines:

$string = 'The $animal says "meow"' $animal = 'cat' 

Then Write-Host $string produce The $animal says "meow" . How can I override $ string, print (or assign to a new variable) The cat says "meow" ?


How annoying ... restrictions on comments make it difficult (if possible) to include code with inverse outputs. Here is an unreleased version of the last two comments I made in response to zdan below:

----------

Actually, thinking about this, I realized that you should not expect that The $animal says "meow" will interpolate without avoiding double quotes, because if it were a double-quoted string, the rating would be violated if double quotes did not run away. Therefore, I assume that the answer will be a two-step process:

 $newstring = $string -replace '"', '`"' iex "`"$string`"" 

One last comment for posterity: I experimented with ways to get everything on one line, and almost everything that you think works as soon as you feed it, i.e. iex, but this works:

 iex ('"' + ($string -replace '"', '`"') + '"') 
+6
source share
5 answers

You can use Invoke-Expression to rename your string - something like this:

 $string = 'The $animal says `"meow`"' $animal = 'cat' Invoke-Expression "Write-Host `"$string`"" 

Note that you need to avoid double quotes (using the return line) inside your string to avoid confusion with the parser. This includes any double quotes in the source string.

Also note that the first command should be a command, if you need to use the resulting string, just connect the output using write-output and assign it to a variable that you can use later:

 $result = Invoke-Expression "write-output `"$string`"" 

As noted in your comments, if you cannot change the creation of the string to avoid double quotes, you will have to do it yourself. You can also wrap this in a function to make it a little understandable:

 function Invoke-String($str) { $escapedString = $str -replace '"', '`"' Invoke-Expression "Write-Output `"$escapedString`"" } 

So now it will look like this:

 # ~> $string = 'The $animal says "meow"' # ~> $animal = 'cat' # ~> Invoke-String $string The cat says "meow" 
+7
source

Probably the easiest way is

 $ExecutionContext.InvokeCommand.ExpandString($var) 
+8
source

Edit: It turns out that the string interpolation behavior is different depending on the version of Powershell. I wrote the best version of xs ( Expand-String ) cmdlet with unit tests to deal with this behavior with this answer about shortening calls to object methods while maintaining context . You can put the following function in the service module somewhere, and it still works when you call it from another module:

 function xs { [CmdletBinding()] param ( #The string containing variables that will be expanded. [parameter(ValueFromPipeline=$true, Position=0, Mandatory=$true)] [string] $String ) process { $escapedString = $String -replace '"','`"' $code = "`$ExecutionContext.InvokeCommand.ExpandString(`"$escapedString`")" [scriptblock]::create($code) } } 

Then, when you need to perform slow expansion of a variable, you use it as follows:

 $MyString = 'The $animal says $sound.' ... $animal = 'fox' ... $sound = 'simper' &($MyString | xs) &(xs $MyString) PS> The fox says simper. PS> The fox says simper. 

$animal and $sound do not expand to the last two lines. This allows you to configure the $MyString extension in front and the delay until the variables have the desired values.

+2
source

I don’t know what version it was introduced, but you can use -f with at least 2. This is the same as calling [String]::Format as far as I can tell.

 PS C:\> $string = 'The {0} says "meow"' PS C:\> $animal = 'cat' PS C:\> Write-Host ($string -f $animal) The cat says "meow" 
+2
source
 Invoke-Expression "`"$string`"" 
0
source

All Articles