ConvertTo-ScriptBlock

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.

image

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().

image

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.

image

UPDATE:

Osin 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
   2:  
   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 are closed