Updated Powershell Scripts

For those who are interested, I just uploaded a bunch of changes to the PowerShell Scripts folder on my SkyDrive. Feel free to download them and use them as you need.

  • find-to-set-alias – Brad Wilson enhanced this function significantly and broke it out into it’s own script. I had a small issue with his version where the folder search may only return a single value, so you can’t treat it like collection. My version wraps that command in @(…) so that you can always treat it like a collection.
  • find-in-path – searches all the folders in your path for a given file name (wildcards supported. Very useful for “where is this app actually installed” kind of debugging.
  • get-git-branch – returns the current git branch of a given folder. Got the idea for this originally from Ivan Porto Carrero.
  • prompt – my powershell prompt. Pretty basic, but it now shows current git branch.
  • elevate-process – create a new PowerShell window or run an app as an administrator. I alias this to su on my machine. I recently reworked the “run an app” part of this script, so it will search the current folder and then the path to run the app you specify.
  • _profile – this is my main profile script, which I share across multiple machines via Mesh. I reworked all my alias setting to use the new find-to-set-alias and moved setting the color of the command window to the top of the script.

Update: I just updated elevate-process again, adding a special clause to handle .bat and .cmd files. cmd.exe seems to ignore the working directory setting, so if your batch file relies on being run from the folder it’s in, it’ll break with elevate-process. That’s annoying. So if you elevate a batch file, the script runs cmd.exe directly and executes the specified batch file after first changing to the current directory. Ugly, but it seems to work.

PowerShell find-to-set-alias

I use Live Mesh to keep my PowerShell scripts folder synced between multiple machine. Some of those machines have different things installed on them or have things installed in different locations. For example, my laptop is x86 while my desktop is x64 so many things on the desktop get installed into c:\Program Files (x86) instead of the plain-old c:\Program Files folder. I wanted my shared profile script to be able to search a set of folders for a given executable to alias, and I came up with the following function.

function find-to-set-alias($foldersearch, $file, $alias)
{
  dir $foldersearch |
    %{dir $_ -Recurse -Filter $file} |
    %{set-alias $alias $_.FullName -scope Global; break}
}

It’s pretty simple to use. You pass in a folder search criteria – it must have a wildcard, or the function won’t work – the file you’re looking for and the alias you want to set. The function finds all the folders matching the $foldersearch criteria, then searches them recursively looking for the $file you specified. Set-alias is called for the first matching $file found – pipeline processing is halted via the break statement.

Here are the find-to-set-aliases I have in my profile:

find-to-set-alias 'c:\program files*\IronPython*' ipy.exe ipy
find-to-set-alias 'c:\program files*\IronPython*' chiron.exe chiron

find-to-set-alias 'c:\Python*' python.exe cpy

find-to-set-alias
    'c:program files*\Microsoft Visual Studio 9.0\Common7' devenv.exe vs
find-to-set-alias
    'c:program files*\Microsoft Visual Studio 9.0\Common7' tf.exe tf

find-to-set-alias 'c:\program files*\FSharp*' fsi.exe fsi
find-to-set-alias
    'c:\program files*\Microsoft Repository SDK*' ipad.exe ipad
find-to-set-alias
    'c:\program files*\Microsoft Virtual PC*' 'Virtual pc.exe' vpc

Python, IronPython and F# aliases, no surprise there. Chiron is the REPL server for dynamic language Silverlight development. Typically, I use Chris Tavares’ vsvars script to configure the command shell for development purposes, but I find it’s nice to have aliases for TF and DevEnv handy at all times.

My Elevate-Process Script

I used to use the Script Elevation PowerToys to provide a simple way to launch an elevated command window from a Powershell prompt. However, that required installing said PowerToys in order to work, which I invariably forgot to install when paving my machine. That got annoying, so I went in search of a pure Powershell solution, which Peter Provost conveniently provided on his blog.

However, one of the benefits of the Script Elevation PowerToys was the ability to launch an admin command prompt in a specific directory – including the current one. I wanted the ability to default to launching Powershell when the user doesn’t specify a command to run. I thought I could just set $psi.WorkingDirectory, but as I’ve described previously, I update $home in my profile script to D:HPierson.Files (I keep my important files on my D: drive so I can pave C: with impunity) then set my current location to $home. So I can’t set the current location by using $psi.WorkingDirectory – it just gets overridden by my profile script.

However, it turns out you can pass arbitrary script code to Powershell via the –Command arguments. You also have to pass –NoExit to keep the command window around. The script passed in via -Command is executed after the profile script, so I can pass in a little script code to set the current location to the right location.

I modified Peter’s elevate-process script to launch a new Powershell command window when zero arguments or one folder argument are passed in. In those cases, elevate-process sets the location to the specified directory (current directory as the default when no arguments are provided) via the –NoExit and –Command arguments.

I’ve posted the script to my SkyDrive as well as providing it below. Feel free to steal use it as you need.

function elevate-process  
{  
  $psi = new-object System.Diagnostics.ProcessStartInfo
  $psi.Verb = "runas";

  #if we pass no parameters, then launch PowerShell in the current location
  if ($args.length -eq 0)
  {
    $psi.FileName = 'powershell'
    $psi.Arguments =  
      "-NoExit -Command &{set-location '" + (get-location).Path + "'}"
  }

  #if we pass in a folder location, then launch powershell in that location
  elseif (($args.Length -eq 1) -and  
          (test-path $args[0] -pathType Container))
  {
    $psi.FileName = 'powershell'
    $psi.Arguments =  
        "-NoExit -Command &{set-location '" + (resolve-path $args[0]) + "'}"
  }

  #otherwise, launch the application specified in the arguments
  else
  {
    $file, [string]$arguments = $args;
    $psi.FileName = $file
    $psi.Arguments = $arguments
  }

  [System.Diagnostics.Process]::Start($psi) | out-null
}

Update: I tried to run my elevate-process script from c:Program Files and discovered a bug. The set-location scripts need the path parameter to be encapsulated in single quotes in order to handle paths with spaces. I’ve updated both the code above as well as the copy on my SkyDrive.

Webdev Script and KillWebDevServer

I was updating my webdev powershell script today. I wanted to add support for a -browser switch that would automatically launch a browser window the way chiron from the Silverlight Dynamic Languages SDK. does. I also set the script to serve up the current directory by default. I posted the new version up on my SkyDrive.

While I was working on the script, I thought about how one might shutdown the WebDev server from the command line. That turned out to be much harder. Basically, you have to look thru all the top level windows for one that has “ASP.NET Development Server” in the window text, then you send that window two messages – WM_QUERYENDSESSION and WM_QUIT. Not sure why the WebDev server uses WM_QUERYENDSESSION to shut down it’s tray icon, but if you look at WebDev.WebServer.exe in Reflector, you’ll see the tray icon form overrides WinProc in order to look for message 0×11, i.e. WM_QUERYENDSESSION.

I threw together a quick little C# console app to shutdown the WebDev server and stuck it up on my SkyDrive as well. Source code is up there too. I had to use a bunch of P/Invokes to make it work, or I would have written it in Powershell or IronPython.

Enjoy.

Some Powershell Scripts

By popular demand, I uploaded a bunch of my scripts to my SkyDrive. Included are:

  • Set Powershell Home – this is actually a zip file that contains the profile redirect script. Unzip this in your Documents folder and change the path in Microsoft.PowerShell_profile to where ever you want your profile and home directories to live.
  • _profile – my “real” profile script. Note, the su function requires the Script Elevation PowerToys in order to work. Also, all the various aliases are hard coded to their location on my machine. You’ll probably want to change them.
  • prompt – my prompt script
  • prepend-path – a script to add a directory to start of my path
  • append-path – like above, but adds the directory to the end of the path
  • append-path-perm – like append-path, but permanently updates the path in the registry

Enjoy.