TheGeekery

The Usual Tech Ramblings

Powershell and Single vs Double Quotes

There can be a lot of confusion over when to use single and double quotes in PowerShell. Not to worry, this confusion carries over in a lot of programming and scripting languages, such as Perl. I figured, after seeing this tweet, to give a quick run down of when to use which one.

This tweet is an example of what to do, and what not to do, but is missing an explanation as to why. So I figured I’d try and explain it, and why there are differences.

Both style quotes delimit a string, however the behavior of them are different1. Generally speaking you want to use single quotes for all strings as this makes the PowerShell processor treat it as a string and that is it. If you use a double quote however, PowerShell reads every character in the string, and looks for characters that can be substituted for a variable defined outside the string, or are actually evaluated as powershell operations. Lets take a look at an example

PS C:\> write-host 'Single quote example'
Single quote example
PS C:\> write-host "double quote example"
double quote example

In these 2 lines, the execution is essentially the same, nothing special happens. Now lets see what PowerShell does when we throw in a variable:

PS C:\> $var = 'Some Variable'
PS C:\> write-host 'single quote with $var'
single quote with $var
PS C:\> write-host "double quote with $var"
double quote with Some Variable

I also mentioned that with double quotes, using $ will cause PowerShell to evaluate functions as well. A simple example would be something like this:

PS C:\> Write-Host 'This is a sum $(7*6)'
This is a sum $(7*6)
PS C:\> Write-Host "This is a sum $(7*6)"
This is a sum 42

Now, if we go back and look at Trevor’s tweet, what we can see is the top “don’t” example has two paths to copy files. Notice something special about the paths, they have spaces in. If you ever work with file paths that have spaces in, you have to quote the entire path. Quoting the string is not the same as quoting the path, and what we can see in this example is that double quotes are used in passing the two paths to the Start-Process command. What doesn’t happen is the double quotes from the variable definition on line 2 and 3 are not carried over into the string as well. So what should be 2 arguments actually becomes 4. Lets see that at work:

PS C:\> $source = "C:\Departments\Marketing Group\"
PS C:\> $Destination = "D:\Departments\Marketing Group\"
PS C:\> write-host "$source $destination"
C:\Departments\Marketing Group\ D:\Departments\Marketing Group\

In arguments, every space is a delimiter for the next argument, so “C:\Departments\Marketing” is one, then “Group" is another, and so on. So what’s going on in the second example? Well, both single and double quotes are being used, and string.format replacements are being introduced2. This allows us to still put variables into single quoted strings, and have double quotes to escape the path.

PS C:\> $source = "C:\Departments\Marketing Group\"
PS C:\> $Destination = "D:\Departments\Marketing Group\"
PS C:\> $ArgumentList = '"{0}" "{1}"' -f $Source, $Destination
PS C:\> write-host $ArgumentList
"C:\Departments\Marketing Group\" "D:\Departments\Marketing Group\"

So now our argument list has quoted paths as required.

There are other ways to use quotes3 that allows you to put double quotes inside a double quoted string. You basically do the quotes twice. So to put a single double quote in string you do them twice. An example would be:

$var = "This is a ""double quote"""

I have an issue doing this because it very quickly and easily becomes confusing trying to keep a track of the number of quotes you have used, and reading the code becomes that much harder.

I made a footnote comment about performance difference as well. In terms of single strings, you don’t really notice a performance difference, but when it comes to itterating over several strings, it starts to build up. Here is a simple test.

$cmd1 = Measure-Command {
	for ($i = 0; $i -lt 100; $i++) {
		write-host 'Test'
	}
}

$cmd2 = Measure-Command {
	for ($i = 0; $i -lt 100; $i++) {
		write-host "Test"
	}
}

$cmd1
$cmd2

Basically just writing out the word test using single and double quotes. Here’s the output:

Days              : 0
Hours             : 0
Minutes           : 0
Seconds           : 0
Milliseconds      : 18
Ticks             : 180830
TotalDays         : 2.09293981481481E-07
TotalHours        : 5.02305555555556E-06
TotalMinutes      : 0.000301383333333333
TotalSeconds      : 0.018083
TotalMilliseconds : 18.083

Days              : 0
Hours             : 0
Minutes           : 0
Seconds           : 0
Milliseconds      : 37
Ticks             : 370316
TotalDays         : 4.28606481481481E-07
TotalHours        : 1.02865555555556E-05
TotalMinutes      : 0.000617193333333333
TotalSeconds      : 0.0370316
TotalMilliseconds : 37.0316

In this simple example over 100 string itterations, and no variable inclusions, you can see that double quotes is just over double the execution time of single quotes. To give some perspective, the average time for a blink is 300-400 milliseconds, so the difference is neglegable, but I’m also running on a fairly powerful machine, and the performance scales with the system being executed on. Give it a shot.

So my general rule of thumb? I try to use single quotes everywhere, and if I have to put variables in the string I tend to use the -f operator. As a side note, I just skimmed a bunch of my older posts and can see where I’ve gone from using double quotes to single quotes as I’ve been going. At some points using -f operator with double quoted strings as well. We all learn new stuffs.

If you want to learn more about quoting strings, you can see get-help about_Quoting_Rules.

  1. And in larger executions impacts run times, but by very small amounts. 

  2. string.format will probably be a whole post all on its own, as it’s an incredibly powerful feature. 

  3. This rule works for single quotes as well. 

Comments