Home PowerShell Internals Special Characters Braces, Parenthesis, And ScriptBlocks
 

Braces, Parenthesis, And ScriptBlocks

What is the difference between braces and parenthesis? There is an easy rule of thumb:

  • In PowerShell, braces enclose code that is NOT immediately executed.
  • Parenthesis, in contrast, enclose code that IS immediately executed.

Just compare these two lines:

  

$a = (Get-Process)
$b = {Get-Process}

  Code in braces is not executed Code in parenthesis is.

When you look at $a and $b after you run the code, you'll see that $a contains the result of Get-Process whereas $b still contains the code. The code in braces was not executed.

When Use Braces, When Parenthesis?

Parenthesis are optional. When you omit them, the code will run anyway:

  

$a = Get-Process

Which raises the question: why use parenthesis at all?

Parenthesis are needed when it is not clear to PowerShell which statement should run first.

So parenthesis are not needed at all if your script code goes step by step. Parenthesis are needed, though, once you start putting more than one expression into one line.

Parenthesis: Needed In Ambiguous Code

Let's assume you wanted to generate lottery numbers.

When you run the following code to generate 6 unique numbers out of 49 (German lottery, feel free to adjust to yours), the result is somewhat unexpected:

PS> Get–Random –InputObject 1..49 –Count 6
1..49

PS>

  This should generate lottery numbers – can you see what is wrong?

The cmdlet returns what you have submitted to -InputObject. No lottery numbers. Why?

Let's switch position and look from PowerShell's perspective: you are submitting an argument to -InputObject. This could be anything. Maybe you meant to put the string "1..49" into your lottery bowl. How would PowerShell know what your input means?

So it takes what you submit, and when you ask for 6 drawings, after the first drawing the lottery bowl is empty, so you get back only one drawing (which coincidentally proves the point that Get-Random will not return duplicates).

To resolve the issue, you need to write unambiguous code that leaves no room for speculation. Use parenthesis to tell PowerShell what to execute first:

PS> Get–Random –InputObject (1..49) –Count 6
19
9
33
35
24
29

PS> 

  This works: first execute the code in parenthesis, then use the results of it

So parenthesis are needed only when code is ambiguous. You can always get rid of parenthesis when you split your job into multiple lines, where each line does only one thing:

PS> $lotteryNumbers = 1..49

PS> Get–Random –InputObject $lotteryNumbers –Count 6
40
44
26
2
30
43

PS>

  Parenthesis are always optional and can be eliminated by writing unambiguous code

As a side effect, while you now no longer can claim to write "one-liners", your code is much more readable and you can better debug it, too.

Braces: Transport Containers For Code

Braces, in contrast, are specialized "transport containers" for PowerShell code. Whatever you put in braces will not immediately execute, but instead can be handed over to a parameter, or stored in a variable.

Code in braces is called a "Scriptblock".

Submitting Code To Parameters

For example, you can place code in parenthesis to submit it to a cmdlet. The cmdlet will then decide if and when it executes the code:

  

$condition = { $_.Length -gt 1MB }

Get-ChildItem -Path c:\windows | Where-Object -FilterScript $condition

  Listing files that are greater than 1MB

Here, Where-Object takes your code and uses it to filter incoming objects. The special variable $_ represents the object that Where-Object needs to look at.

So if Get-ChildItem finds 50 files in your windows folder, Where-Object will execute the code that you submitted 50 times, and $_ will be the respective file that was piped to Where-Object.

Submitting Code To Control Structures

Likewise, you can use braces to submit code to control structures like this one:

  

[int]$hour = Get-Date -Format HH

if ( $hour -gt 15 )
{
  'Afternoon'
}
else
{
  'Before Afternoon'
}

  Evaluating the current hour

The statement "if" is a condition, and as you can see, the condition is evaluated in parenthesis (executed immediately).

There are two scriptblocks (code that does not immediately execute), and you leave it to "if" and the result of the evaluation to determine which one will be executed.

Here is another one:

  

# milliseconds:
$duration = 500

for ($hz = 1000; $hz -lt 4000; $hz+=250)
{
    "Frequency: $hz Hz"
    [System.Console]::Beep($hz, $duration)
}

  Playing sounds in different frequencies

Here, the "for" loop executes the code in parenthesis to determine how often the loop should run. The code in braces, however, is not executed immediately (and once).

Instead, it is executed when "for" thinks it is time to execute, and it is executed multiple times, as often as the loop wants it to run.

Deferred Execution

You can run scriptblocks manually, too. So if you wanted, you can place code into a scriptblock and run it whenever you need to:

  

$code = {
  $time    = Get-Date
  $hour    = $time.Hour
  $minute  = $time.Minute
  $seconds = $time.Second
  $speaker = New-Object -ComObject Sapi.SpVoice
  $null    = $speaker.Speak("It is now $hour $minute and $seconds seconds.")
}

& $code
Start-Sleep -Seconds 4
& $code 

  Let your computer tell you the current time – as often as you want

To run a scriptblock, use the call operator ("&"). The call operator isolates the code, so any variable the code defines will be removed when the code finishes:

PS> $time

PS> 

If you run the scriptblock with dot-sourcing instead (replace "&" by "."), then the code will not be isolated (it will run in your – the callers – context), and after the code is done, all of its variables will still exist:

PS> . $code

PS> $time

Samstag, 25. Januar 2014 16:29:12

PS> 

Functions – Named Scriptblocks

If you just had a slight de-ja-vu, then you are right: calling a scriptblock manually is really pretty close to calling a powershell function – and it actually is the same.

PowerShell functions are just NAMED scriptblocks. So with the keyword "function", you can bind a name to a scriptblock, and whenever you call this name, PowerShell will execute the associated scriptblock:

  

function Test-Time
{
  $time    = Get-Date
  $hour    = $time.Hour
  $minute  = $time.Minute
  $seconds = $time.Second
  $speaker = New-Object -ComObject Sapi.SpVoice
  $null    = $speaker.Speak("It is now $hour $minute and $seconds seconds.")
}

Test-Time 

  functions are just named scriptblocks

In fact, the same calling convention you just used with scriptblocks is true for functions as well. If you run a function as-is, or if you run a function with the call operator ("&"), it runs isolated in its own context.

If you run a function dot-sourced, its variables will show up in your (the callers) context:

PS> Remove–Variable –Name minute

PS> $minute

PS> Test–Time

PS> $minute

PS> & Test–Time

PS> $minute

PS> . Test–Time

PS> $minute
33

PS> 

  Calling a function with "." will run the function in your context

What About Parameters?

If PowerShell functions and scriptblocks are the same, what about parameters?

The truth is: parameter support is built into scriptblocks, not functions. So when you use functions with parameters, functions simply use the parameter mechanism built into its scriptblock.

Here is a function with parameters:

  

function Say-Something
{
  param
  (
    [Parameter(Mandatory=$true)]
    $Text
  )
 
  $speaker = New-Object -ComObject Sapi.SpVoice
  $null    = $speaker.Speak($Text)
}

Say-Something -Text 'Hello' 

  A simple function with parameters

When you run the function, it will speak the text you submitted.

Actually, the parameter -Text was made mandatory, too, so if you do not submit this parameter, the function will ask for it:

PS> Say–Something
cmdlet Say–Something at command pipeline position 1
Supply values for the following parameters:
Text: Hello, how are you?

PS> 

Now let's do the same with an anonymous script block:

  

$code = {
  param
  (
    [Parameter(Mandatory=$true)]
    $Text
  )
 
  $speaker = New-Object -ComObject Sapi.SpVoice
  $null    = $speaker.Speak($Text)
}

& $code -Text 'This works, too!'

  Scriptblocks can receive parameters, too

Scriptblocks work the same, because they are the same. They even support mandatory parameters:

PS> & $code
cmdlet  at command pipeline position 1
Supply values for the following parameters:
Text: Oh my god

PS> 

What next?

There is a lot more to say, but for today, that is it. Check back for more details on parenthesis, braces, scriptblocks and PowerShell intrinsics!