O'Reilly logo

Monad (AKA PowerShell) by Andy Oakley

Stay ahead with the world's most comprehensive technology and business learning platform.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, tutorials, and more.

Start Free Trial

No credit card required

Work with Special Characters

Although MSH does a good job of taking input and parsing it to get an understanding of intent, there are times when it needs some help. For example, consider the simple copy-item cmdlet, which takes two parameters: source and destination. In the typical case, usage is very simple and easy to follow:

    MSH D:\MshScripts> copy-item file1 file2

But what happens when filenames contain spaces?

    MSH D:\MshScripts> copy-item my file1 file2

Does this mean copy my file1 to file2, copy my to file1 file2, or something else? Clearly, we need some way of identifying where one parameter ends and another begins. These quoting rules go beyond cmdlet parameters and are used consistently throughout the shell.

Next, we'll look at some of the different types of strings available, their delimiters, and some special character sequences employed by MSH that allow us to express exactly what we mean.

How Do I Do That?

Let's start with an easy example:

    MSH D:\MshScripts> "In double quotes"
    In double quotes
    MSH D:\MshScripts> 'In single quotes'
    In single quotes

Does this mean we can use single quotes and double quotes interchangeably? Not exactly. MSH makes a subtle but important distinction between the two: single quotes are used to represent a literal string (one that will be used exactly as is) whereas MSH looks through strings inside double quotes and replaces any variable names with their values in a process known as variable expansion:

    MSH D:\MshScripts> $myName = "Andy"
    MSH D:\MshScripts> "Hello, $myName"
    Hello, Andy
    MSH D:\MshScripts> 'Hello, $myName'
    Hello, $myName

Single quotes are allowed inside of strings enclosed in double quotes and vice versa. This is often convenient when quotation marks are needed within a string.

    MSH D:\MshScripts> $myName = "Andy"
    MSH D:\MshScripts> 'He said "Hello, $myName"'
    He said "Hello, $myName"
    MSH D:\MshScripts> "He said 'Hello, $myName'"
    He said 'Hello, Andy'

What if I really wanted to output He said "Hello, Andy" with double quotes instead? Nested quotation marks aren't going to cut it here, so we somehow need to include the double quote character inside the string.

MSH enables us to do this by escaping the double quote character, giving special instructions on how to interpret it differently than usual. When the grave accent character ('), also known as a backquote or backtick, is used inside a string, MSH understands that the character immediately following it has a special meaning.

    MSH D:\MshScripts> "He said '"Hello, $myName'""
    He said "Hello, Andy"
    MSH D:\MshScripts> "Col 1'tCol 2'tCol 3"
    Col 1    Col 2    Col 3

What Just Happened?

Because a string can be defined in MSH in different ways, the quoting rules are used to instruct the shell exactly how it should work with the content between the quotation marks and whether it should be passed straight through or undergo some processing first. The difference between the two main cases is in how MSH treats the $ sign and any variable names that follow it. If the single quotation marks are used, MSH does not inspect the string and uses it as is. By using double quotation marks, you are implicitly asking the shell to do a search on any variable names within the string, replacing them with the current value of the variable.

Variable expansion in double-quoted strings consistently follows some simple rules. If the $ sign appears in the string, any legal characters following it are assumed to refer to a variable name. MSH will look forward until it hits something that doesn't qualify (such as a space, newline, tab, comma, etc.) and use everything up to that point as the variable name. The shell then looks up the value of that variable, converts it to a string, and places the value into the original string:

    MSH D:\MshScripts> $alpha = 2
    MSH D:\MshScripts> $alphabet = 9
    MSH D:\MshScripts> "$alphabet"
    9                # matches $alphabet not $alpha, otherwise this would
                     # return "2bet"
    MSH D:\MshScripts> "some value=$undefinedVariableName"
    some value=

Because MSH looks forward until it hits a nonalphanumeric character, a special syntax is used for the expansion of more complex variables such as arrays and hashtables. Parentheses can be used immediately after the $ sign to enclose the entire variable name and any indexers necessary for correct evaluation:

    MSH D:\MshScripts> $arr = @("first","second")
    MSH D:\MshScripts> "$arr[0]"
    first second[0]            # Oops!
    MSH D:\MshScripts> "$($arr[0])"
    first

The ability to nest different styles of quotation marks inside each other is often a handy shortcut, but it is not a universal solution. For example, the string 'He said "I'm here"' is invalid because there is an uneven number of single quotation marks in the string. In anything but simple cases, it's better to rely on escape characters for including quotation marks inside a string.

With both of the approaches available, should single or double quotes be used? This depends on several factors. In clear-cut cases, the decision is based on the rules: if the string contains a $ sign that needs to be expanded, use double quotes; if the $ sign is intended literally, single quotes can be used or the character can be escaped as '$. In the general case, however, it often comes down to personal preference. Most of the examples we've seen throughout the book so far have used double quotes, even when variable expansion is not expected. This makes it very easy to add a variable into a string at a later date and have it expanded automatically with a negligible difference in performance.

The escape character (') has a number of meanings depending on its location and usage. When used on the command line, it indicates that the character immediately following it should be passed on without substitution or processing, which is helpful when a character has meaning to MSH but isn't meant to be used in that fashion. For example:

    MSH D:\MshScripts> write-host Use the -Object option
    write-host : A parameter cannot be found that matches parameter 'Use'.
    At line:1 char:11
    + write-host  <<<< Use the -Object option
    MSH D:\MshScripts> write-host Use the '-Object option
    Use the -Object option
    MSH D:\MshScripts> copy-item my' file1 file2
    # equivalent to copy "my file1" "file2"

When used within a string (of either single or double quote variety), MSH knows to replace the escape character and the character immediately following it with a special character. Table 4-2 lists some of the escape sequences you're likely to need.

Table 4-2. Common MSH escape sequences

Character

Meaning

''

Single quote

'"

Double quote

''

Grave accent

'$

Dollar symbol

'0

Null character (different than $( ))

'a

Alert (beep)

'b

Backspace

'f

Form feed

'n

Newline

'r

Carriage return

't

Tab

'v

Vertical tab

What About...

... Do Unix shells use the grave accent character for something else? Yes. In many shells, the grave accent character is used for command substitution when the output of a command is assigned to a variable and used for further processing.

For example, in bash:

    PROCESSOR='uname -p'
    NOW='date'
    echo "This machine is a $PROCESSOR"
    echo "Current time is $NOW"

Command substitution is a critical tool when data is passed around as text without any definition of structure. Because MSH works more comfortably with structured data, it's usually more convenient to draw information from the shell, another cmdlet, or the .NET Framework than it is to draw information from the textual output of another command.

The equivalent MSH script would read:

    $processor = $Env:PROCESSOR_IDENTIFIER
    $now = get-date
    write-host "This machine is a $processor"
    write-host "Current time is $now"

Of course, given that $processor and $now represent structured data, we're in a position to work with information such as $now.Year without any further effort. In any case, the direct equivalent to the '...' syntax is $(...), which will cause MSH to evaluate the expression in parentheses (e.g., "The current date is $(get-date)").

... What about "here strings "? The term here string refers to a third technique for defining strings. Two markers are used to represent the start and end of the string (@" and "@, respectively), and anything that stands between them, including newline characters, is included in the definition:

    MSH D:\MshScripts> $a = 10
    MSH D:\MshScripts> $longString = @"
    >>First line
    >>Second line
    >>Variable expansion $a
    >>"@
    >>
    MSH D:\MshScripts> $longString
    First line
    Second line
    Variable expansion 10
    MSH D:\MshScripts>

MSH does provide equivalent command substitution syntax for cases in which it is necessary to capture the textual output of a command:

    $pingTarget="127.0.0.1"
    $pingOutput = $(ping $pingTarget)

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, interactive tutorials, and more.

Start Free Trial

No credit card required