Remoting Quoting

The information in this article was written about the Community Technology Preview (CTP) of Windows PowerShell 2.0. This information is subject to change in future releases of Windows PowerShell 2.0.

On This Page

Remoting Quoting Quotation Marks in Windows PowerShell Quotation Marks in Remote Commands Try It For More Information

Remoting Quoting

By June Blender Windows PowerShell Documentation Team

I love quotes. Here's my current favorite:

"I am somehow less interested in the weight and convolutions of Einstein's brain than in the near certainty that people of equal talent have lived and died in cotton fields and sweatshops." -- Stephen Jay Gould

But quotes, and quotation marks, can be tricky in Windows PowerShell. And they can be even trickier in remote commands that use the Invoke-Expression cmdlet.

Fortunately, the interpretation of quotation marks in Windows PowerShell is governed by some very simple rules. Once you master the rules, they become your tools, instead of your obstacles.

You can quote me on that.

So, today, we'll examine the use of quotation marks in Invoke-Expression commands in Windows PowerShell 2.0 CTP. But first, the warning:

WARNING: Windows PowerShell 2.0 CTP is just a preview (That's the P in CTP). The final version of Windows PowerShell 2.0 might be significantly different.

By the way, to understand this article, you need to be familiar with the basics of remote commands in Windows PowerShell 2.0 CTP. You can find that information here.

Quotation Marks in Windows PowerShell

First, let's review the basic distinction between single and double quotation marks in the Windows PowerShell language. It concerns the interpretation of variables.

  • Double quotes. When a command or expression that includes variables is enclosed in double quotes ("), the variables are replaced by their values before the command is executed.

  • Single quotes. When a command or expression that includes variables is enclosed in single quotes ('), the variables are not replaced by their values. Instead, they are interpreted literally.

There's actually a lot more to it – nested quotes and doubled quotes and escaped quotes and quotes in here-strings -- but these are the basics. For the rest, at the Windows PowerShell prompt, type:

  • get-help about_quoting_rules

Quotation Marks in Remote Commands

Quotation marks are really important in remote commands, because the value of the Command parameter of Invoke-Expression must be a string. If the imbedded command includes spaces, parentheses or special characters, you need to enclose it in quotation marks.

And the type of quotation marks that you choose make all the difference.

  • The outermost quotation marks – the ones that make the value of the Command parameter a string -– are interpreted on the local computer and then discarded. The remote computer receives the command without the outermost quotation marks. That is, the remote computer receives a command, not a string that contains a command.

  • Double quotes. When the outermost quotation marks in an Invoke-Expression command are double-quotes ("), any variables in the command are replaced by their values on the local computer, before the command is sent to the remote computer.

  • Single quotes. When the outermost quotation marks in an Invoke-Expression command are single quotes ('), the variables are not replaced by their values on the local computer. Instead, they are sent to the remote computer to be interpreted on the remote computer.

Try It

Let's try some examples. In these examples, the remote computer is running Windows Vista with Service Pack 1 and the local computer is running Windows XP with SP2.

We'll start with some simple examples. When the embedded command (the value of the Command parameter) doesn't include spaces, parentheses, or a variable, no quotation marks are needed.

invoke-expression -computername Server01 -command get-process

In commands that don't include variables, double or single quotes have the same effect. These two commands are effectively the same.

invoke-expression -comp Server01 -command "get-process powershell"

invoke-expression -comp Server01 -command 'get-process powershell'

However, the quotation marks do matter when the command contains a variable. For example, let's display the value of the $home variable on the local and remote computers. $home is a Windows PowerShell "automatic variable" that stores the full path the current user's home directory, that is, %homedrive%\%homepath%. (You can find out about automatic variables by typing : "get-help about_automatic_variables".)

This command displays the value of $home on the local computer.

$home

Here's the output:

C:\Documents and Settings\user01

Now, let's run the same command on the Server01 remote computer.

invoke-expression -computername Server01 -command "$home"

Here's the output:

Invoke-Expression : The term 'C:\Documents' is not recognized as a cmdlet, function, operable program, or script file.
Verify the term and try again.
At line:1 char:18
+ invoke-expression <<<<  -computername Server01 -command "$home"

Several things went wrong here, but one was that the $home variable was interpreted on the local computer. (There is no "Documents and Settings" directory on Windows Vista.) Also, because the local home directory path contains spaces, Windows PowerShell interpreted the substituted value as more than one string.

Let's try the remote command again. This time, we'll enclose the command in single quotes.

invoke-expression -computername Server01 -command '$home'

Here's the output:

C:\Users\user01.DOMAIN01

We should always verify that the output actually came from the remote computer. To do this, run the command again and save the results in the $output variable.

$output = invoke-expression -computername Server01 -command '$home'

Now, display the value of the PsipHostEntry property of the $output variable. PsIpHostEntry is a property added to remote results that contains information about the computer on which the command ran.

$output.psiphostentry

Here are the results. They confirm that the command in single quotes retrieved the value from the remote machine.

ComputerName    Aliases         Addresses
------------    -------         ---------
Server01        {}              {172.30.181.234}

For most commands, you will use single quotes, because you usually want the variables to be evaluated on the remote computer. But if you want the variable to be evaluated on the local computer, use double quotes. For example, the first command in the following pair of commands stores a list of services in a variable; the second command gets those services on the remote computer. Because the variable exists on the local computer, you want the string of services to be substituted for the variable before you send it to the remote computer.

$services = "wmi*, winrm, netlogon, plugplay"

invoke-expression -computername Server01 -command "get-service $s"

Here's the output:

Status   Name               DisplayName
------   ----               -----------
Running  Netlogon           Netlogon
Running  PlugPlay           Plug and Play
Running  WinRM             Windows Remote Management (WS-Manag...
Stopped  wmiApSrv           WMI Performance Adapter

If you run the command with single quotes, the Get-Service cmdlet returns all of the services on the remote computer, because $s is null.

invoke-expression -computername Server01 -command 'get-service $s'

Now, don't generalize too much. If you tried to run this same command on the local computer without Invoke-Expression, it would fail, because all of the services are in a single string. The outermost quotes are not deleted from the value of a Get-Service command, like they are from an Invoke-Expression command. As a result, the Get-Service cmdlet would be looking for a service called "wmi*, winrm, netlogon, plugplay" and it wouldn't find one.

To run the same command on the local computer, each service would have to be in its own string, like this:

$services = "wmi*", "winrm", "netlogon", "plugplay"
get-service $services

Another helpful way to use quotation marks is to set the boundaries of the invoke-expression command. Consider the following commands:

invoke-expression -comp Server01, Server02 -command  get-service  | group-object Status

invoke-expression -comp Server01, Server02 -command 'get-service  | group-object Status'

These commands are both valid and they produce different results. Which one is right? It depends on what you want.

The first command uses the Invoke-Expression cmdlet to run a Get-Service command on the Server01 and Server02 computers. This command does not require quotation marks, because "Get-Service" does not have any spaces or special characters. The pipeline character (|) sends the services retrieved by the Invoke-Expression command to the Group-Object cmdlet, which groups the services by status. The Get-Service command runs on Server01n and Server02. The Group-Object command runs on the local computer. Here's the command and the result:

invoke-expression -comp Server01, Server02 -command  get-service  | group-object Status

Count Name                      Group
----- ----                      -----
  141 Running                   {System.ServiceProcess.ServiceController, System.ServiceProcess.ServiceController, S...
   99 Stopped                   {System.ServiceProcess.ServiceController, System.ServiceProcess.ServiceController, S...

The second command uses quotation marks to enclose the Get-Service and Group-Object commands. The quotes make the whole pipeline part of the value of the Command parameter of Invoke-Expression cmdlet. As a result, both Get-Service and Group-Object are run on Server01 and Server02. So, the services are grouped separately on each of the remote computers, not on the local computer.

Here's the command and the result:

invoke-expression -comp Server01, Server02 -command 'get-service  | group-object Status'

Count Name        Group
----- ----        -----
   62 Running    {System.ServiceProcess.ServiceController, System.ServiceProcess.ServiceController, S...
   47 Stopped    {System.ServiceProcess.ServiceController, System.ServiceProcess.ServiceController, S...
   79 Running    {System.ServiceProcess.ServiceController, System.ServiceProcess.ServiceController, S...
   52 Stopped    {System.ServiceProcess.ServiceController, System.ServiceProcess.ServiceController, S...

The result is really more apparent when you use the Format-Table cmdlet to add the PsIPHostEntry property of the remote results to the table. Note that Format-Table runs on the local computer. You cannot format on the remote computer, at least not in Windows PowerShell 2.0 CTP.

invoke-expression -comp server01, server02 -comm 'get-service | group-object status' | format-table count, name, group, psiphostentry

PSIPHostEntry      Count Name            Group
-------------      ----- ----            -----
server01              62 Running         {System.ServiceProcess.Ser...
server01              47 Stopped         {System.ServiceProcess.Ser...
server02              80 Running         {System.ServiceProcess.Ser...
server02              51 Stopped         {System.ServiceProcess.Ser...

Once you learn the rules, you can run very complex commands on remote computers without error. This command gets error events that were recorded in the System log on the Server01 remote computer today. Just remember to enclose the command in single quotes.

invoke-expression -computer Server01 -command 'get-eventlog -log system |
where {($_.Type -eq "Error") -and ($_.TimeWritten.date -eq ([datetime]::now.date)) `
 -and ($_.message -like  "*failed*")}'

The best way to learn how to use Invoke-Expression is to play with it. Try running your favorite commands on a remote computer. If you get unexpected results, consider changing the quotation marks.

Oh, and did I mention my other favorite quote:

"One of the symptoms of an approaching nervous breakdown is the belief that one's work is terribly important." -- Bertrand Russell

Hmmm. Might be time to take a break.

For More Information

To read about remoting and quoting, see the following Windows PowerShell 2.0 CTP help files:

  • about_remoting

  • about_runspace

  • invoke-expression

  • about_quoting_rules

For information about automatic variables, like the $home variable, see:

  • about_automatic_variables