When to Quote in PowerShell

The one question about PowerShell that trips up almost everyone is about when and how to quote strings. Because PowerShell replaces the old command shell, it has to be able to work the way that it did with string parameters, but it also has to behave like a .NET scripting language to replace VBA. PowerShell grapples with this apparent contradiction, and manages to square the circle. Michael Sorens explains the how and when of PowerShell quoting.

When do you need to use quotes or not in PowerShell? And when you need them, do you need single quotes or double quotes? At first glance, you would think that PowerShell’s own help page about_Quoting_Rules would answer both of those, but it does not! It covers part of the topic, primarily string interpolation and here strings (both of which we’ll discuss below). In other words, it assumes you need quotes and then explains some of the factors affecting which-single or double-to choose. But it completely ignores the bigger picture. Meanwhile over on StackOverflow you will find When do I need to use quotes in PowerShell? That poses the canonical question yet barely lands a glancing blow on the answer. So, this article is here to bring together the entire topic of quoting in PowerShell in one place for a handy reference source.

Do You Need Quotes?

Let’s start with an example. Consider listing the details of a couple files with the Get-ChildItem cmdlet-I use its ls alias to minimize the presence of the cmdlet name, focusing just on the elements that we are quoting or not. Which of these is valid PowerShell and lists the details of the two files in question?

The answer is (A), (C), and (D); assuming the appropriate files exist, all of those return the exact same result (they will give different error messages if the files do not exist). Because both cases (C) and (D) work correctly, you can infer that when the cmdlet is given first, you do not need quotes but you may use them. So let’s focus on the two primordial cases: case (A) where quotes are needed and case (D) where quotes are not needed:

To understand the difference requires an awareness of PowerShell’s two parsing modes, command parsing and execution parsing. These two modes work together to allow PowerShell as a scripting language to peacefully co-exist with PowerShell as a shell language allowing you, the user, to be able to move code between a script and the command line and have it just work. That is, in a script (program) having to type string literals with quotation marks like this…

is quite reasonable. However, at the command line that would be annoying to some, intolerable to others. You should be able to just type this:

Furthermore, like any good shell, you must be able to type expressions like 5 + 2 and get a response of 7, or type “hello world” and get hello world echoed back. Yet when you type a cmdlet name, e.g. Get-Process, it should execute the command, not just echo the characters Get-Process. Simple, you say, because omitting the quotes signals that it is a command to execute, right? But what if you want to execute a command with a space in it, perhaps C:\Program Files\xyz.exe ? Well, without quotes PowerShell would just try to execute C:\Program with Files\xyz.exe as its first argument. So clearly it must have quotes because of the space, but if you try “C:\Program Files\xyz.exe” it just gets echoed back. Whew!

PowerShell’s two parsing modes allow you to untangle and manage your commands and your scripts with ease. Per Keith Hill’s reference article, Understanding PowerShell Parsing Modes:

“First [you] need to parse like a traditional shell where strings (filenames, dir names, process names, etc.) do not need to be quoted. Second [you] need to be able to parse like a traditional language where strings are quoted and expressions feel like those you would find in a programming language. In PowerShell, the former is called Command parsing mode and the latter is called Expression parsing mode. It is important to understand which mode you are in and more importantly, how you can manipulate the parsing mode.”

The rules for selecting a mode (or determining which you are in) are simple:

  • Start with a letter, underscore, ampersand, dot or backslash => command parsing mode
  • Start with anything else => expression parsing mode

Note that any leading whitespace in the line (or more precisely, in the statement) does not count in determining the start, so “start” above really means the first non-whitespace character in the statement.

Examples:

# Text Result
1 Get-ChildItem … letter => cmd mode => run Get-ChildItem
2 5 + 2 number => exp mode => evaluate the expression 5 + 2
3 “hello world” quote => exp mode => evaluate, i.e. echo the string
4 “C:\Program Files\xyz.exe” quote => exp mode => evaluate, i.e. echo the string – aargh!
5 & “C:\Program Files\xyz.exe” ampersand => cmd mode => run C:\Program Files\xyz.exe – huzzah!
6 C:\Program` Files\xyz.exe Letter => cmd mode => run “C:\Program Files\xyz.exe” – again huzzah!

The last three lines above show the problem of spaces within an executable path and how to address the issue: either (row 5) start the line with the call operator ( & ) and quote the string, or (row 6) escape any spaces within the string by preceding each with a backtick (`).Escaping a space makes it act like any other character in the string so then you do not need to surround the string with quotes!

You can also freely intermix command parsing with expression parsing as needed. Consider this short line:

It starts in command parsing and PowerShell recognizes a conditional (if) statement. Parentheses start the mode determination process over, so inside the parentheses the exclamation mark forces expression parsing, negating something, that something again starting with parentheses, and again restarting mode determination. Inside those parentheses the letter “G” signals command parsing, so Get-ToolsLoaded is executed, its result negated, then the conditional proceeds or not. Here’s one more example-this is a real line of code I pulled out of one of my open-source scripts-that shows multiply-nested expressions with different parsing modes (“exp” for expression mode parsing; “cmd” for command mode parsing):

2264-1-e224a89f-266d-44b3-9e88-a735a8a27

Do You Need to Quote Arguments to a Command?

With an awareness of parsing modes, now go back to the two equivalent ways to fetch file details:

Applying the parsing mode rules, case (A) starts in expression mode. It evaluates “a.txt”, “b.txt” as an expression and returns a list; that list is fed into a pipeline as input to ls (Get-ChildItem). The pipeline is yet another place that restarts parsing determination, so ls is recognized as a command and executed. In case (D), it starts in command parsing mode and again executes ls. That’s great… but why don’t the arguments to ls need quotes? The answer can be gleaned from the PowerShell Language Specification Version 3.0, section 8.2:

“A command invocation consists of the command’s name followed by zero or more arguments. The rules governing arguments are as follows: … An argument that is not an expression, but which contains arbitrary text without unescaped white space, is treated as though it were double quoted.” [emphasis added]

The phrase “text without unescaped white space” is rather convoluted and I claim, a bit misleading. What that is saying is:

  1. Text without whitespace is considered quoted, i.e. a string literal like abc.
  2. Text with escaped whitespace is also considered a string literal, like two` words.
  3. And incidentally, if there is unescaped whitespace (like two words) the whitespace is considered a boundary between arguments, so apply (1) and (2) to each argument separately: both two and words are considered string literals by (1).

The definition of expression is detailed in section 7 of the specification but the rules are similar to parsing mode determination; suffice to say that a.txt is not an expression, so it is as if you typed “a.txt”.

Of course, if one of your file names contains spaces, then you must explicitly include the quotes. It is unreasonable to expect PowerShell to be able to figure out that “a file.text” is all one name here:

So you must do this:

Again, if you prefer to avoid quotes, you always have the option of escaping any embedded whitespace by preceding each space with a backtick:

Do You Need to Quote the Target of an Assignment Statement?

Given the two assignment statements shown, which is legitimate?

Per PowerShell Language Specification Version 3.0, section 7.11, assignment statement syntax is:

    expression   operator   statement

The operator in this case is the equals sign (=). The right-hand side of the equals is thus the statement component. Typically you think of a statement as starting at the beginning of a line but this is an example to the contrary; statements may occur even within a line! Beginning a statement implies, as you have seen, beginning of parsing determination. In (A) the statement begins with a quote, so it is considered expression mode and the literal hello is recognized. In (B), however, the first character of the statement is a letter, meaning command mode applies, and it tries to execute hello, which is not what you want.

Thus, in assignments, quotes are required.

Do You Need to Quote Components of an Expression?

Given the following expressions, which are legitimate?

Each line begins with a quoted string so when parsing determination ensues, all of these lines begin in expression parsing mode. The real question here is what happens after the operator, be it an arithmetic operator (A and C) or a comparison operator (B and D)-are quotes required? The answer here is straightforward if you think about it: you start in expression mode when encountering the left-hand argument, continue and encounter the operator, then continue and encounter the right-hand argument. Nothing in there restarts parsing determination so the right-hand argument must still be a legitimate expression, which means if you want a string literal you have to dress it up as such-with quotes-so A and B are valid. (If, on the other hand, you wanted to execute a function named foo, you then need to dress it up as such-in expression’s clothing, as it were-by putting it in a subexpression where parsing determination can restart, i.e. : aaa‘ + (foo).

Which Quotes to Use?

You have determined that you need quotes; the next determination is whether you need single quotes (‘) or double quotes (“). The rule for this is very simple to state but needs a bit of explanation, which follows: use single quotes for literal (or static) strings and double quotes for interpolated (or interpreted) strings.

Let’s explore this in detail. For this first example I will borrow from PowerShell’s help page, about_Quoting_Rules, reformatting a bit to add clarity. Assume you have defined the variable $i to be 5 before executing any of these:

# Text Result
1a “The value of $i is $i.” The value of 5 is 5.
1b ‘The value of $i is $i.’ The value of $i is $i.
1c “The value of `$i is $i.” The value of $i is 5.

In this first group (1a) shows the effect of a double-quoted string. Any embedded variables are interpolated (or interpreted if you prefer) so the output is different from the input. The (1b) row, on the other hand, shows that with single quotes the string is completely static: the output is identical to the input and no interpolation occurs. The (1c) row shows that you can actually mix interpolation with literals: precede any dollar sign with a backtick to prevent it from initiating an interpolation for that one instance.

# Text Result
2a “The value of $(2+3) is $i.” The value of 5 is 5.
2b ‘The value of $(2+3) is $i.’ The value of $(2+3) is $i.
2c “The value of `$(2+3) is $i.” The value of $(2+3) is 5.

This second group shows the analogous expressions but this time using a $(…) subexpression. Within a double quoted string, anything inside the subexpression is evaluated, showing that you can have much more than just a simple variable name interpolated. The above example is contrived of course just to keep the subexpression terse. A common need for such a subexpression, though, comes when you want to interpolate the property of an object. Say you have a $process object ($process = get-process -name winword) and you want to embed the company name associated with the process in a string.

# Text Result
3a “Word by $process.Company” Word by System.Diagnostics.Process (WINWORD).Company
3b “Word by $($process.Company)” Word by Microsoft Corporation

Everybody tries approach (3a) first-but do not try it with your boss looking on or you will have one of those awkward moments.:-) String interpolation starts at the dollar sign and stops at the first character that cannot be part of a simple variable name, in this case the period. So $process alone is interpolated and, being a complex object, you get its type name and the name of the process. Use a subexpression to get what you really want, as in (b).

Besides variable interpolation, double-quoted strings also include special character interpretation. That is if you type `t inside a double-quoted string it will output a tab. Within a single-quoted string, special characters are not interpreted, but output just as you typed them. See about_Special_Characters for other such special characters. I have highlighted the `t here just for clarity:

# Text Result
4a “Item1`tItem2`tItem3” Item1     Item2     Item3
4b ‘Item1`tItem2`tItem3’ Item1`tItem2`tItem3

Including Quotes within Quotes

Sometimes you just need to put literal quotes within a string literal. The table below shows all the combinations and how you need to manipulate the input to get the desired output. In (5a) and (5b) you can see that including the other quote-the one you are not using to delimit the string-requires no special action. (6a) and (6b) show how to put double quotes within double quotes. You have two choices: either repeat the double quote character (6a) or escape the double quote character (6b) with a backtick. Finally putting single quotes within single quotes allows only one option, doubling the single quote (7a). You cannot escape with a backtick (7b) because inside a single quoted string everything is a literal-including a backtick-so that single quote in the middle ends the string prematurely!

# Text Result
5a “Single quote ‘ within double quotes” Single quote ‘ within double quotes
5b ‘Double quote ” within single quotes’ Double quote ” within single quotes
6a “Double quote “” within double quotes” Double quote ” within double quotes
6b “Double quote `” within double quotes” Double quote ” within double quotes
7a ‘Single quote ” within single quotes’ Single quote ‘ within single quotes
7b ‘Single quote `’ within single quotes’ error

A special note on (7a): It looks like there is a double quote in the middle that somehow, magically, becomes a single quote in the output. But not so! That is simply two single quotes juxtaposed. Thus, the technique is the same for single-quoted or double-quoted strings: double the same quote character to get a single one in the output.

Multi-Line Strings

PowerShell lets you create strings spanning multiple lines (as a variety of scripting languages do) using a here string. A here string, or here document, is simply a piece of text embedded within your program that is treated as if it came from a separate file; in other words, it’s right here. PowerShell’s here strings can be created with single quotes or double quotes and behave as you should now expect: single-quoted they are pure literals; double-quoted allows for interpolation of variables, subexpressions, and special characters.

A here string must be formatted thusly:

That is, nothing but whitespace and may follow the opening token (@”). And most important, the closing token must be absolutely the first character on the line-no preceding whitespace allowed! The value of the here string is everything between the two demarcated <ENTER>s shown above.

You can actually embed line breaks in a regular string just as simply as a here string, but a here string has one main advantage: you can freely embed quotes just like any other character (8a, 8c), no doubling or escaping necessary!

# Text Result
8a @”
Word
by “$process.Company”
“@
Word
by “System.Diagnostics.Process (WINWORD).Company”
8b @”
Word
by $($process.Company)
“@
Word
by Microsoft Corporation
8c @’
Word
by ‘$process.Company’
‘@
Word
by ‘$process.Company’

A Word About Format Strings

For completeness, it is worth mentioning the standard .NET format string. In C#, you can, for example do something like this…

…which might print something like this:

What Console.WriteLine is effectively doing is using the String.Format method, which takes a format string containing place holders followed by a list of objects to populate those placeholders. You can use String.Format in PowerShell with the rather terse -f operator. This is the PowerShell equivalent:

Effectively that is doing exactly the same as this:

…so you might jump to the conclusion that you must use a double-quoted string when using the -f (String.Format) operator. But no! In this instance either single quotes or double quotes will do:

# Text Result
9a “{0}, your balance is {1}. (Status {2})” -f $name, $balance, $status Welcome Mr. Smith, your balance is $100. (Status OK)
9b ‘{0}, your balance is {1}. (Status {2})’ -f $name, $balance, $status Welcome Mr. Smith, your balance is $100. (Status OK)

Why is the single-quoted string in this peculiar case acting like active code instead of just a string literal? The answer is that it is not; it really is just a string literal, character for character. Those placeholders are plain text, as far as the string is concerned; it is the String.Format method that reads that string and treats those placeholders differently.

Summary

Strings are such a vital concept in any language that it is important to have a good grounding in their fundamentals. PowerShell being a scripting language tends to have more flexibility surrounding their use, covering a wide range of scenarios. In this article I have brought together all of that in one place to make it easy to learn everything you need, and to have a single reference source to come back to when you have questions about quoting strings. So go back and look at your own code; chances are you will find you can remove a lot of clutter by removing many unnecessary quotes!

2264-sorenspdf-350x220.jpg

A reference wallchart of the information in this article is available to download and print here