Out of the box in PowerShell, you cannot cast a string to a scriptblock.

   1: 81# [scriptblock]"A String that can't be a SB"
   2: Cannot convert value "A String that can't be a SB" to type "System.Management.Automation.ScriptBlock". Error: "Invalid
   3: cast from 'System.String' to 'System.Management.Automation.ScriptBlock'."
   4: At line:1 char:14
   5: + [scriptblock] <<<< "A String that can't be a SB"

This is actually a good thing. To be able to take a string and arbitrarily execute it could definitely be a security risk. However, there are definitely times when you need to do this.

In my case, I am parsing a Windows Sharepoint list that has a set of ScriptBlocks and our code snippets that I need to execute. However, when I pull them from SharePoint, they are strings.

Originally, I was using Invoke-Expression because everything was running locally. But now, with PowerShell remoting becoming available with V2, I wanted to start using invoke-command. Invoke-Expression takes a string for the –command parameter. Invoke-Command takes a ScriptBlock as a –ScriptBlock paramater

Using an old V1 method, you can use the following function to convert a string to a script block.

   1: function ConvertTo-ScriptBlock {
   2: param ([string]$string)
   3: $scriptblock = $executioncontext.invokecommand.NewScriptBlock($string)
   4: return $scriptblock
   5: }

Below is a screen shot of using invoke-command with a string vs the scriptblock that we get back from this function.


As you  know, Windows 7 (Demoed at PDC) has PowerShell enabled out of the box. Turns out there is a slightly easier way to do this in the new win7 build and it  will very likely be here in CTP3 as well.

There is now a static method in the ScriptBlock class called Create().


So we can write a new V2 function (lets make it an advanced function for fun) using this new method.

   1: function Convert-StringToScriptBlock {
   2: param(
   3: [parameter(ValueFromPipeline=$true,Position=0)]
   4: [string]
   5: $string
   6: )
   7: $sb = [scriptblock]::Create($string)
   8: return $sb
   9: }

Now with the ability to specify the [parameter] attribute and pass it in parameters like ValueFromPipeline we can easily pass in our string from the pipeline or directly via the parameter like we did in the V1 version.



Oisin Grehan has meticulously pointed out that the original name of my function, convert-stringToScriptBlock did not adhere to the naming standard for Cmdlets. Note the following examples:

   1: PS C:\> gcm convert* -CommandType cmdlet | ft Name
   3: Name
   4: ----
   5: ConvertFrom-Csv
   6: ConvertFrom-SecureString
   7: ConvertFrom-StringData
   8: Convert-Path
   9: ConvertTo-Csv
  10: ConvertTo-Html
  11: ConvertTo-SecureString
  12: ConvertTo-Xml

For convert functions, we should use ConvertTo and ConvertFrom as the “verb”. I wanted to point this out and thank Osin because I completely acknowledge that I am guilty as charged and I think it is a BIG DEAL to stick with standards. Consistency is imperative as PS Adoption rates increase.

In my humble opinion, I  say functions should use the standard verbs just like Cmdlets. You can create shortcuts all day long with aliases. This will become even more important with the introduction of Modules and advanced functions

Comments (4) -

Hey Andy,

ConvertTo-ScriptBlock would be the preferred name for such a function according to the powershell grammar nuts


Ok, I'm confused.  Creating a script block from a string is as simple as enclosing it in curly braces.  Why do you need a function with these methods?

PS > $a = { "hi" }
PS > &$a

Fair enough. I just wanted a function to explicitly make the cast. This just seems more readable than surrounding it with curly brackets to force the cast.

I supose I could have just as easily done this

PS C:\> $string = "foo bar"
PS C:\> $sb = [scriptblock]{$string}
PS C:\> $sb.gettype().FullName
PS C:\>

The reason for all this craziness was that invoke-command takes a scriptblock where as invoke-expression takes a string. I am in the process of updating a script to use a bunch of the new V2 features, particularly remoting, so I was replacing what I was doing with invokie-expression with invoke-command so I could run commands on remote servers. I wanted a nice clean, easy to read function.


George B. Calverley 8/13/2009 11:08:07 PM

Another (useful) case where casting from string won't work is when trying to create a conditional body for a where-object script block. e.g

# Depending on the value of $ModifiedSince, the script block changes.
#$ModifiedSince = $null                          # all files
$ModifiedSince = ([datetime]::now).AddHours(-5)  # files modified in the last 5 hours.

$s =  "`$_ -is [System.IO.FileInfo]"
if ($ModifiedSince)
    $s += " -and `$_.LastWriteTime -ge `$ModifiedSince"
$sb = [scriptblock]::Create($s)
get-childitem | where-object $sb

Comments are closed