At the end of 2007, I made a New Year’s resolution to start using PowerShell as my command window. Amazingly, that’s the only resolution I’ve ever kept in my life! Shocking, isn’t it? What’s even better is that by using PowerShell, it made me taller, better looking, and smarter, too. Well, not the two former, but definitely the latter applies. It’s been an interesting year and now that I have moved past the complete novice stage to the rank beginner I thought I’d mention some of the good things I’ve learned and some of the things that trip me up so that it might help others out there.
Nothing impresses me more about PowerShell than the team themselves. The frequency and enthusiasm they put into blog posts, blog comments, and forum answers is astounding. Nearly every time I’ve been perplexed on some aspect of PowerShell a quick internet search reveals the answer. The team’s activity is matched by the community as well. Sites like PoshCode.org are a huge help for learning.
One extremely helpful thing Jeffrey Snover has been doing is adding special keywords to various posts and responses. A quick search for PSMDTAG: yields all sorts of high quality information about PowerShell. For frequently asked questions, use PSMDTAG:FAQ. Some of the other useful codes are PSMDTAG:DOTNET, PSMDTAG:TYPE:WMI, PSMDTAG:TYPE:COM, and PSMDTAG:SECURITY. Jeffrey Snover is building the semantic web, one post at a time!
Most of my daily work is in a regular PowerShell window, but when I need to write a script or explore advanced features of PowerShell, I find myself turning to Idera’s PowerShellPlus Professional Edition. The main reason is the advanced auto complete for not only PowerShell types, but COM objects and WMI as well. Those data tips listing the parameter types were well worth the money. The built in debugger is quite good and I love being able to stop in the middle of a debugging session and execute commands to check script state and change variables. If you’re new to PowerShell and want to get up to speed in a hurry, you need to check out PowerShellPlus.
While PowerShell 2.0 will have a built in Integrated Scripting Environment (ISE) it looks like we’re getting a real editor and debugger, but I suspect there will be plenty of opportunity for 3rd party software to extend and provide value. Many people are getting very excited about the V2 ISE, the remoting support, script cmdlets, and background jobs, the thing that’s caught my eye is comment-based help. Here’s an example (taken from here):
function Get-Everyone([switch]$fromDomain) {
#.Synopsis
# Gets all users
#.Description
# Queries WMI to get all users. To save time, queries
# are local unless the -fromDomain switch is used
#.Parameter fromDomain
# If set, gets domain users as well as local accounts
#.Example
# # Get All Local Accounts
# Get-Everyone
#.Example
# # Get All Local & Domain Accounts
# Get-Everyone –fromDomain
$query = “Win32_UserAccount”
if (-not $fromDomain) {
$query+= ” WHERE LocalAccount=’True'”
}
Get-WmiObject $query
}
I just totally love that! The function help is right in the comments so no more extra files and it helps when reading code. As I’m all about maintenance, anything that can help totally rocks in my book. Now I the PowerShell team would get the V2 Beta shipped so we can all shift over to it. I want it!
While I’ve converted completely to PowerShell there are a few things about it that always trip me up. The biggest one is where PowerShell does not set the current working directory. As you’re navigating around the file system, you’ve come to expect that the directory shown in your prompt is where your files will be written. For example, executing the following does exactly what you expect it to, write file to the c:junk directory.
[C:Junk]>dir | Out-File test.txt
[C:Junk]>dir test.txt
Directory: Microsoft.PowerShell.CoreFileSystem::C:Junk
Mode LastWriteTime Length Name
—- ————- —— —-
-a— 12/31/2008 2:58 PM 8088 test.txt
However, executing the following shows the problem.
[C:Junk]>$myFile = [System.IO.File]::CreateText(“Test2.txt”)
Exception calling “CreateText” with “1” argument(s): “Access to the path ‘c:Test2.txt’ is denied.”
At line:1 char:39
+ $myFile = [System.IO.File]::CreateText( <<<< “Test2.txt”)
Using Process Explorer, the current directory of my PowerShell.exe instance is definitely C:. PowerShell doesn’t call SetCurrentDirectory in the file system provider because it’s a process wide and with PowerShell V2 supporting background jobs inside the process, that could screw them up. That makes sense, but it’s the inconsistency between the baked in PowerShell commands and everything else, such as .NET and external programs, that’s the problem. I learned about this from Lee Holmes.
After fumbling around with this numerous times, I thought I’d mention it to save others some time. Below is a function I have in my profile that I can call whenever I need the full path to the current location that handles the case if the file doesn’t exist. This has served me well.
function Get-FullPath ( [string] $fileName )
{
# The easy case is if it exists. Just call Resolve-Path.
if ( Test-Path $fileName )
{
return $(Resolve-Path $fileName)
}
else
{
# The file doesn’t exist.
# Look to see if the caller has passed in a drive letter.
$rootPath = [System.IO.Path]::GetPathRoot($fileName)
if ( $rootPath -ne “” )
{
# Return what the user passed in.
return ( $rootPath )
}
# There’s no drive letter so make it relative from the current location.
$fullName = [system.IO.Path]::Combine($(Get-Location) , $fileName)
return ( $fullName )
}
}
The other issue I bump into frequently is PowerShell improperly parsing the command line options to an external program. It’s taken me all year to remember to put the grave accent (`) in front of command line arguments to external programs so they are parsed correctly. Given my 4NT muscle memory, it took me a while to get a feel for how PowerShell parses the command line. Save yourself many hassles by reading Keith Hill’s Effective PowerShell Item 10: Understanding PowerShell Parsing Modes extremely carefully. Now if PowerShell can get real command line editing such as typing the first few characters of a previous command and pressing SHIFT+UP ARROW to jump right to that command in the history, that’d be wonderful. While I’m dreaming how about CTRL+V paste support? Tab expansion in the middle of a long command would be great as well. I haven’t seen any of these mentioned in discussions of the V2 release so I’m not holding my breath.
This is definitely not the PowerShell team’s fault, but it’s annoying that the TFS PowerToy PowerShell snapin is for 32-bit only. I went nuts trying to figure out how to get it loaded into my 64-bit machines. Manually running InstallUtil.EXE was giving me a bizarre error of a bad image exception. After staring at the screen for a while, I finally got the bright idea to run CORFLAGS.EXE which showed all the TFS PowerShell DLLs had the x86/32-bit flag set. Nothing like wasting two hours trying to figure out why I was getting an odd error message.
I wanted to use the TFS PowerShell extensions but I didn’t want to have to remember to start the x86 version of PowerShell to run them. I came up with a hacky way of spawning the x86 version, but Vivek Sharma demonstrated a much cleaner way just yesterday:
if ($env:Processor_Architecture -ne “x86”)
{
write-warning “Running x86 PowerShell…”
&”$env:windirsyswow64windowspowershellv1.0powershell.exe” `
-noninteractive -noprofile `
-file $myinvocation.Mycommand.path -executionpolicy bypass
exit
}
# The rest of your code here….
If you haven’t made the jump to PowerShell, I hope you’ll take hope from my experience. If an old grizzled 4NT user like me can make the jump, you can too! With all the server products being built onto of PowerShell, the cool diagnostic system in Win7, and everything else, you’re going to have no choice but to start sometime soon. Trust me you’ll be better off when you start using it.
Oh, a while back I posted a script that would download the Sysinternals collection .ZIP file. Now that the wonderful live.sysinternals.comtools Web-Dav server is running, here’s the updated version of that script. Now all those great tools are just a ROBOCOPY away!
#################################################################
# Get-SysInternalsTools.ps1 – John Robbins – john@training.atmosera.com
#
# Using RoboCopy to copy the SysInternals tools.
# RoboCopy is part of Vista and Server 2K8. You’ll have to get
# it from the Resource Kit on older operating systems.
#################################################################
param ( [string] $dirWrite )
# Always make sure all variables are defined.
Set-PSDebug -Strict
function Usage
{
“”
“Smartly downloads all the tools from Sysinternals”
“”
“Usage: Get-SysInternalsSuite <directory>”
“”
” <directory> : The directory where you want to put the tools.”
” If this directory does not exist, it will be created.”
” -? : Display this usage information”
“”
“”
exit
}
function CreateDirectoryIfNeeded ( [string] $directory )
{
if ( ! ( Test-Path $directory -type “Container” ) )
{
New-Item -type directory -Path $directory > $null
}
}
##################################################################
# Main execution starts here.
# Check for the help request.
if ( ( $Args -eq ‘-?’) -or ( ! $dirWrite ) )
{
Usage
}
$paramLog = @”
Param dirWrite = $dirWrite
“@
Write-Debug $paramLog
# If the extract directory does not exist, create it.
CreateDirectoryIfNeeded ( $dirWrite )
# Do it!
robocopy live.sysinternals.comtools $dirWrite