PowerShell Mini-game: Equality

§ April 9, 2013 14:06 by beefarino |

In light of the upcoming community Scripting Games, I have a PowerShell brain teaser for you.  

Query: In PowerShell, when is the following statement true? Explain why.

     ( $a -eq $b ) -ne ( $b -eq $a )

Submit your answer as a comment, or post it on your own blog and provide a link in the comments of this post.  The best explanation (as chosen by me) will receive a complimentary single-user license for SeeShell (a $249 value).  You have until April 16, 2013 to submit your answer; the winner will be announced April 17, 2013.



To Confirm or Not to Confirm

§ March 3, 2013 06:41 by beefarino |

SNAGHTML34f13bfNo, wait – I mean to be confirmed or not to be confirmed.  Or is it “I am confirming” or “I am not confirming?”  Oh hell…

Yep, here is another PowerShell idiom on which I stub my metaphorical cognitive toe every time: the –confirm parameter.  Let’s see if this new habit of writing about it changes my brain.

The –confirm parameter is one of those ubiquitous common parameters that shows up in most cmdlets.  Even the ones you make yourself.  Check it:

function test-confirm {
  [cmdletbinding(SupportsShouldProcess=$true)]
  param()
  
  process { 
    if( $pscmdlet.shouldProcess( "test","test") )
    {
        write-host "processed" 
    }
      
  }
}

> test-confirm -?

test-confirm [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPrefe
rence>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuff
er <Int32>] [-WhatIf] [-Confirm]

That SupportsShouldProcess parameter of the CmdletBinding attribute is what makes the –confirm parameters show up (sidebar: it also enables the –whatif parameter).  It’s a simple switch parameter, making its use trivial:

test-confirm -confirm

The question is: what am I actually saying when I use the –confirm parameter?  Here are two choices:

  1. I am confirming this operation, so do not prompt me for confirmation.
  2. I wish to be asked to confirm this operation, so please prompt me for it.

I always always think it’s 1, when it’s actually choice 2.  It’s a strange semantic, and in truth I can think of few situations where you want to force a confirmation prompt.  Generally the –confirm parameter is used to suppress these prompts by explicitly setting the switch to false:

test-confirm -confirm:$false

This feels awkward and in my opinion reads poorly – something about saying “no” to a parameter named –confirm and then having the command execute silently feels wrong.  I think it’s because I think of “confirming” as something I’m supposed to do, when it’s actually something PowerShell does.  In other words, the act of confirming is PowerShell asking me whether to proceed, and not my answer to that question.

Learning More

The PowerShell help contains a ton of info on –confirm and the other common parameters.  Check out these help topics to learn more:

  • about_CommonParameters
  • about_Functions_CmdletBindingAttribute


The Difference between your Current Directory and your Current Location

§ February 27, 2013 11:45 by beefarino |

35479106It’s time for another entry in the trying-to-keep-myself-from-making-this-mistake-all-the-time series.

PowerShell has a notion of your current location.  You can see this using the $pwd automatic variable or the get-location cmdlet:

PS C:\Users\Jim\Documents> $pwd

Path                                                                                                                   
----                                                                                                                   
C:\Users\Jim\Documents                                                                                                 


PS C:\Users\Jim\Documents> get-location

Path                                                                                                                   
----                                                                                                                   
C:\Users\Jim\Documents                                                                                                 

This location is also displayed in the default prompt.  This path is used by PowerShell to resolve relative paths at the level of the PowerShell API.

Applications have a notion of the current directory.  This is the directory used to resolve relative paths at the level of the Windows API.

How you Get Burned

Your current location may or may not be the same as your current directory.

Allow me to elaborate…

If you write a file using a PowerShell cmdlet like out-file, the relative path you specify will be resolved using the current location:

PS C:\Users\Jim\Documents> 'hello world' | out-file foo.txt
PS C:\Users\Jim\Documents> ls foo.txt


    Directory: C:\Users\Jim\Documents


Mode                LastWriteTime     Length Name                                                                      
----                -------------     ------ ----                                                                      
-a---         2/27/2013  10:50 AM         28 foo.txt                                                                   

I told the out-file cmdlet to write the file foo.txt, and it create that file in my current location – c:\users\jim\documents.  Make sense? 

Now, let’s try the same thing using the .NET Framework directly:

PS C:\Users\Jim\Documents> [io.file]::writealltext( 'bar.txt', 'hello world' )
PS C:\Users\Jim\Documents> dir bar*
PS C:\Users\Jim\Documents> dir ../bar*


    Directory: C:\Users\Jim


Mode                LastWriteTime     Length Name                                                                      
----                -------------     ------ ----                                                                      
-a---         2/27/2013  11:04 AM         11 bar.txt                                                                   

This time I used the System.IO.File.WriteAllText static method, specifying the relative path bar.txt.  But the file was written into another directory! 

Why?  Because the WriteAllText method uses paths relative to my current directory, not my current location.  In this case, they’re different:

PS C:\Users\Jim\Documents> [environment]::currentdirectory
C:\Users\Jim
PS C:\Users\Jim\Documents> get-location

Path                                                                                                                   
----                                                                                                                   
C:\Users\Jim\Documents                                                                                                 

The fact is, PowerShell doesn’t actively move the [environment]::currentdirectory as you navigate around the drive:

PS C:\Users\Jim\Documents> cd .\project
PS C:\Users\Jim\Documents\project> [environment]::currentdirectory
C:\Users\Jim
PS C:\Users\Jim\Documents\project> cd /
PS C:\> [environment]::currentdirectory
C:\Users\Jim

This becomes a problem when you use PowerShell to run an application or invoke parts of the .NET framework – the output doesn’t go where you’d expect it to go.  Generally speaking, these things will work off of the current directory and have no concept of your current location in PowerShell.

Things You Can Do

First, you can force PowerShell to update the current directory each time the current location changes.  Just customize your prompt function so it sets [environment]::currentDirectory to the current filesystem location:

   1:  function prompt {
   2:      $p = get-location -PSProvider filesystem | select -exp path;
   3:      [environment]::CurrentDirectory = $p;
   4:      return "Jim is Awesome!!!1 $pwd> ";
   5:  }

Line 2 gets the current location for the FileSystem PowerShell Provider.  We have to specify the FileSystem provider because PowerShell lets you do crazy things like set the current location to the registry, and we can’t very well set the current directory to such a path.  Line 3 updates the current directory so it matches the current location.  This happens each time each time the prompt is written by the current host, so the current directory will be updated after each command.  That means when you change location, the current directory will immediately follow suit.

Second, if you author cmdlets that rely on paths, and you end up using these paths in .NET framework calls, I would strongly recommend that you resolve the PowerShell paths into full file system paths before passing them on to the framework:

// ...
[Parameter(Mandatory = true)]
public string FilePath { get; set; }

protected override void ProcessRecord()
{
    var fullPath = this.GetUnresolvedProviderPathFromPSPath(FilePath);
    Bitmap bitmap = CreateBitmap();
    bitmap.Save( fullPath );
}
// ...

Finally, if you find your self expecting a file to appear in your current location and it doesn’t, check the current directory before you start throwing things:

PS C:\Users\Jim\Documents> test-path bar.txt
False
PS C:\Users\Jim\Documents> test-path ( join-path ([environment]::currentdirectory) bar.txt)
True


StudioShell 1.5 is Available

§ February 6, 2013 14:07 by beefarino |

I’m happy to say that a new release of StudioShell is up, along with a new version of the StudioShell.Beta nuget package!

The big things in this release:

  1. Support for Visual Studio 2012
  2. Support for PowerShell 3.0
  3. Support for use in the ISE

In addition there are tons of itty bitty bug fixes and incremental improvements. 

This release is still classified as a beta.  I want to collect usage information from VS2012 and/or PowerShell 3 users before marking the release as stable, and there are some documentation gaps that need to be filled.  That said, the 1.5 release is still preferred over the existing 1.2 and 1.3.1 packages for stability and features.

Oh, and I went ahead and started pushing the StudioShell.Contrib project.  There isn’t must up there - at the moment it contains a few “helper functions” that I commonly use, and I will shortly push some Psake-related functions along with some contribution guidelines.  My hope is that others will fork the project and contribute their own pieces.

Enjoy!