Archive

Archive for February, 2009

PowerShell Deep Dive: Understanding Get-Alias, wildcards, escape characters, quoting rules, literal vs. non-literal paths, and the timing of string evaluation

February 18, 2009 1 comment

The following question recently came up on a mailing list I follow:

When I am trying to get the definition for the alias "?", I need to escape it because if not it works like a wildcard.  That is ok.

But why do I have to use SINGLE quotes, and why do DOUBLE quotes fail to escape?  I thought it should be the other way around.

For example, this works:

Get-Alias ‘`?’

This does not work:

Get-Alias "`?"

Why?

This question was prompted by Aleksandar’s blog post about the problem.  There are quite a few things to consider here to understand what is going on.

First, as the author of the question pointed out, in the Get-Alias cmdlet the question mark character is a wildcard character.  That means if you simply call Get-Alias with a question mark (Get-Alias ?), you will get all one-character aliases because the question mark will match any character.  That will happen regardless of whether you use single quotes, double quotes, or no quotes.  So the question mark must be escaped to tell Get-Alias to treat it as an actual question mark, and not a wildcard question mark.

Second, in PowerShell a single-quoted string is a literal string, so characters are taken as is.  A double-quoted string is a special string where the contents are evaluated to determine what the actual string is (see Get-Help about_quoting_rules for more details).  In a double-quoted string, any variables or subexpressions (identified by their prefix of $) are evaluated and the results are placed in the string.  Also, any escaped characters are evaluated and the results are placed in the string.  The standard way to escape a character is to precede it with a backtick (`).

Third, if you escape any non-escape character by preceding it with a backtick, the result is simply that non-escape character.  The backtick is discarded.

Fourth, there is a difference between escaping escape characters when evaluating a double-quoted string and escaping wildcard characters in cmdlet that accept strings containing wildcards.  Both are escaped the same way, but understanding the timing of their evaluation and knowing what characters they consider to be escape characters is very important.  Double-quoted string evaluation happens outside of a cmdlet, before it is called.  Wildcard string evaluation happens inside of a cmdlet, once it is called.

And lastly, Get-Alias works just like the Path parameter set variant of Get-Item (that is to say, it works just like Get-Item when you call Get-Item with the Path parameter), returning multiple results when unescaped wildcard characters are in the search string and there are multiple matching aliases.  Get-Alias is a little simpler to use on a general basis, but if it wasn’t there you could just use Get-Item instead.

Now that we’re armed with that knowledge, lets break down the command we’re having a hard time with.

Get-Alias "`?"

Here are the results of that command in PowerShell:

image

What’s happening here?  When this command is executed, the first thing PowerShell has to do is evaluate the double-quoted string and get the result string that comes out of that evaluation, before the cmdlet is called.  As I explained above, any non-escape character that is escaped in a double-quoted string is simply evaluated as the non-escape character.  In our string, the question mark is being escaped, but that isn’t an escape character when it comes to string parsing (use the command Get-Help about_escape_character to get the list of escape characters in double-quoted strings), so the result of the evaluation is simply ‘?’.  When that string is then passed to Get-Alias, the Get-Alias cmdlet thinks we’re looking for any single-character aliases, so that’s what it gives us in the output.

What we need to do is make sure that the results of the evaluation of the double-quoted string are such that the question mark is preceded by a backtick so that it is escaped when it is passed into the Get-Alias cmdlet (string evaluation timing is important, remember?).  To do that, we need to know how to generate a single backtick in the result returned from a double-quoted string evaluation.  The backtick is an escape character itself as well as the character used to escape other characters (we learned this when we looked at the about_escape_character helpfile, mentioned earlier), so we can create a backtick in a double-quoted string by escaping it with itself, like this: "“".

Now that we know how to escape the backtick so that it is passed into the Get-Alias cmdlet, we can change our problematic command we were using by escaping the backtick in the double-quotes and get the results we wanted in the first place:

image

It is important to note that a non-quoted string is treated as a double-quoted string in PowerShell when it is evaluating a command that it is about to run, so if you wanted to do this without quotes at all, you would still have to escape the backtick, like this:

image

The last thought I want to leave you with is about Get-Item.  I mentioned earlier that Get-Alias works just like the Path parameter set variant of Get-Item.  Both of these need to have wildcard characters escaped if you want to find an alias that contains a wildcard character.  What I didn’t mention is that Get-Item has something that Get-Alias does not: a LiteralPath parameter set variant.  The easiest way to retrieve an alias containing a wildcard without worrying about escaping any characters is to simply use Get-Item with the alias PSDrive, like this:

image 

This method requires the use of the LiteralPath parameter name and the alias PSDrive name, but it gives you exactly what you want, without having to worry about most of what I tried to show you here, and it’s easy.  Still, I had to show you the details first…a solid foundation in understanding how these things work goes a long way when writing and troubleshooting PowerShell scripts.  If you want it even easier, you could create a simple Get-LiteralAlias function that removes the need to use the Alias PSDrive name and calls Get-Item using the LiteralPath parameter set variant behind the scenes.  You could also create a Get-Alias function that has a Literal switch parameter so that you could simply indicate whether you wanted to call Get-Alias or Get-LiteralPath behind the scenes.  With PowerShell, there are definitely no shortage of options when you don’t have the functionality you are looking for the way you expect it to be there.  I’ll leave the exercise of creating the functions up to you, if you think it’s worth it (after all, there is only one default alias with a wildcard character in its name).

Armed with all of this knowledge, it becomes completely obvious why an unusual alias created with the New-Alias command (which doesn’t support wildcard characters) like this:

$foo = ‘bar’
New-Alias "“?$foo" ‘bar’

can be retrieved using either of the following Get-Alias commands:

Get-Alias "““?$foo"
Get-Alias "““`?$foo"

Right? :)

Kirk out.


Share this post:

PowerShell Quick Tip: Create aliases to facilitate invocation of PowerShell script (ps1) files

February 4, 2009 3 comments

I was just reading Steve Murawski’s latest blog post which contains a script called ConvertTo-Function that allows you to generate a function from a script file.  The intent is to allow you to have scripts you don’t call as often, and therefore don’t load into your profile, but when you need them you can use this script to easily generate functions that wrap them so that you don’t need to think about the path beyond the initial call to the ConvertTo-Function script.  That’s one way to facilitate this, but I have something I use quite commonly in my own environment that works much better for me for this scenario: aliases.

Aliases can reference a path to a script file.

This capability is often overlooked because aliases are most commonly used and thought of as terse command names.  Using them to reference a path to a script file is quite useful because it allows you to keep all of your scripts, complete or in development, in a common location.  You can control which ones are loaded in your profile by only creating aliases for those scripts.  Alternatively, for scripts that you really don’t use very often, you could have a function in your profile to create the aliases to those scripts on an as needed basis (which sounds a little like the Add-Module capabilities in PowerShell v2).  In either case, using aliases to reference script files is very handy because you don’t need to worry about whether the scripts you want to invoke are in a folder in your path environment variable or not.  You simply need to create an alias to the full path and then you can use that alias to invoke your script.

For example, assuming you had a script with the path C:\MyScripts\Miscellaneous\Do-Something.ps1, you could invoke it using the absolute or relative path, or if C:\MyScripts\Miscellaneous happened to be in your path environment variable you could simply invoke it using the Do-Something command.  But if you don’t have your script path in your path environment variable and/or you don’t want to change that variable, aliases give you the same capability on a temporary basis for your current PowerShell session (and only in your current scope by default if you want it really temporary).  Using our example script path, all you would have to do is create a new alias with this command:

New-Alias Do-Something C:\MyScripts\Miscellaneous\Do-Something.ps1

That allows you to invoke your script any time you want to by simply using the Do-Something alias, as long as it is created and visible in the current scope.

Kirk out.


Share this post:

VMware Infrastructure PowerPack 2.1 released

February 1, 2009 3 comments

Hot on the heels of the new release of the VMware VI Toolkit, I just finished uploading version 2.1 of the VMware Infrastructure Management PowerPack for PowerGUI.  This PowerPack facilitates management and automation of VMware Infrastructure servers using the VMware VI Toolkit with PowerGUI’s extendable administrative console.

Version 2.1 of the VMware Infrastructure Management PowerPack includes the following highlights:

  • Significant performance improvements when loading datacenters and clusters.
  • New top-level container nodes to facilitate viewing objects without having to browse into the Managed Hosts node.
  • Links allowing you to browse into log files from hosts.
  • Support for the VMware VI Toolkit 1.5 release.
  • VMotion support for virtual machines.

In addition to these changes, several links have been added and quite a few defects have been fixed.

You can learn more about this PowerPack, including version history and other details here.  You can download it from that location as well.

As always, feedback on this PowerPack and all others is welcome and appreciated.  If you want to see something in the PowerPack, just ask!  You can either contact me directly (see my about page), or you can post your request using the PowerGUI forums.  We’re listening!

Kirk out.


Share this post:

Follow

Get every new post delivered to your Inbox.

Join 945 other followers