Channels ▼
RSS

.NET

PowerShell for Developers with Admin Tasks


PowerShell is the best unkept secret in the Windows developer world. Although it is highly lauded by its users, it has not gained a lot of traction with the Windows developer crowd. It is all the rage with the Windows admin crowd, however. Powershell is tightly integrated with the operating system, .NET framework, and most Microsoft server products (SQL Server, IIS, TFS, etc).

In this article, I discuss why PowerShell should be used more by developers and I show you some useful stuff you can do with PowerShell. Finally, I'll go over my PowerShell profile and show how I use PowerShell every day to make my life easier.

PowerShell: The Conspiracy Theory

Historically, Windows had the worst shell ever: command.com, cmd.exe coupled with the worst terminal Window ever. That had two important outcomes:

  • A strong aversion to working from the command-line on Microsoft operating systems
  • Excellent GUI tools to perform tasks

PowerShell 1.0 became available in 2006 for Windows XP. However, it became ubiquitous (that is, installed by default) only in Windows 7 and Windows Server 2008 R2. During the five year delay, the trend toward mobile apps, Web applications, and the cloud emerged and the attention of the developer crowd shifted away from PowerShell.

But this neglect is unwarranted. Let me quickly show you why before diving into the meat of the article. Here are some cool one-liners.

Generate the integer between 5 and 20 (inclusive):

5..20

Pick a random number between 1 and 100:

1..100 | Get-Random

Play a video:

(New-object –COM WMPlayer.OCX).openPlayer("Your video")

Calculate total size of current directory in MB:

"{0:N2}" -f ((Get-ChildItem . -Recurse | Measure-Object -Property Length -Sum).Sum / 1MB)

Progress bar while computing total size of directory (OK, not a one liner, but very cool)

$files = Get-ChildItem . -Recurse
$total = 0
For ($i = 1; $i -le $files.Count-1; $i++)
{ 
    Write-Progress -Activity "Calculating total size..." -status $files[$i].Name -percentComplete ($i / $files.Count * 100)
    $total += $files[$i].Length
    Start-Sleep -Milliseconds 50
}

Write-Host "Total size: $($total / 1MB)"
#Write-Host "Total size: {0:N2}MB" -f ($total / 1MB)

Introductory PowerShell

PowerShell is object-based. UNIX (Linux, Mac OS X) shells are text-based. The idea is that using the shell pipeline on the UNIX variants, you can glue together any number of small tools that eat and spit text lines to create arbitrarily complex work-flows. This works and gives you infinite flexibility. But, at a price. The price is that you often have to perform some elaborate text processing between commands in the pipeline to adapt the output of one command to the input expected by the next one. This can lead to cumbersome and brittle pipelines that are both wonderfully concise, and yet completely unintelligible for the uninitiated.

PowerShell is different: Commands eat and spit out .NET objects. This approach is better suited to the Windows environment where many of the system facilities are exposed through APIs and object models (Registry, COM, .NET, ADSI, WMI, Certificate store). This aspect makes it easy to user PowerShell for GUI front ends. For example, Figure 1 shows a form with a check box, date picker and OK button that was easy to assemble using free tools such as Sapien's PrimalForms GUI generator. Dr. Dobb's has previously discussed the ins and outs of writing GUIs in Powershell by hand.


Figure 1: A GUI applet written in Powershell.

The "How" of PowerShell

PowerShell was designed for Windows system administrators and power users. It was optimized for interactive use on the command-line first, and for scripting second. If you have some experience with UNIX shells, PowerShell will sometimes feel very familiar and sometimes quite alien. The reason is that the UNIX-based operating systems and Windows evolved along very different trajectories. UNIX shells were optimized to manage a text-based ecosystem in which everything is or is made to appear as a text file. PowerShell was designed to manage the API-based Windows ecosystem.

The PowerShell team did a good job evaluating the needs of their target audience. They also looked at how sysadmins operate in the UNIX environment, then compared this environment to the Windows environment, distilled all this input, and came up with an architecture and design for a consistent, flexible, and extensible command execution engine and shell.

Before we get started, let's do a quick refresher on Powershell: $variable describes a variable name. Most commands rely on Cmdlets, which are named objects such as Set-ExecutionPolicy. Many of these commands have multiple built-in aliases. For example, the Get-ChildItem command has three official aliases: dir, ls, and gci. The dir alias is there to ease the transition for old Dos/Windows hands. The ls alias is to placate the UNIX crowd, and gci is there because if you have no previous experience with other shells, then it's the "PowerShell-y" way to define aliases (first letter of every word in the command). This is a very thoughtful, considerate, and humane approach that, when added to the fact that PowerShell is case insensitive, makes reading other people's code (or even your own code from a few months ago) challenging.

Finally, a word about operators: The comparison operators in PowerShell use mnemonics like -lt (less than), -gt (greater than), and -eq (equal to), instead of the familiar symbols = (or ==), !=, <, and >. The reason is a tip of the hat to ancient tradition. In particular, the long-time shell output redirection symbol is '>'. PowerShell designers wanted to keep this tradition, so they created the set of comparison operators -lt, -eq, -neq, -le, -ge, which takes a little getting used to.

Starting Up

Presuming you have PowerShell installed, you need to set the security policy to allow the running of scripts. You do this by typing Set-ExecutionPolicy RemoteSigned. This will allow you to launch PowerShell scripts locally. PowerShell, being an administrator's tool first and foremost, is configured by default to not allow running scripts as a security measure. This is a sensible default on remote machines, but you will definitely want to run scripts on your machine.

Next, set your profile. The PowerShell profile is where you can collect all the stuff you use commonly, so it's available in every session. There could be several profile files with the more specific files overriding more general ones. To create a PowerShell profile that applies to all users type:

  new-item -path $env:windir\System32\WindowsPowerShell\v1.0\PowerShell_profile.ps1
  -itemtype file -force

The path to the current profile is always in the $profile global variable. You can verify it exists by typing: Test-Path $profile.

Once you have a profile, you can edit the file and add your stuff. Remember to execute the profile after each change, so your changes will be available in the current . $profile session.

Don't try to learn PowerShell in a methodological manner. Play with it. Even playing may feel strange at first. The best way to go is to look for cool stuff other people have done and just stick it in your profile and use it.

Once you feel comfortable in the PowerShell environment, start expanding your horizons. Check your profile and look at all the stuff you stole from other people. Read a little about the commands they use and their parameters. Try to understand the tricks and idioms they use. PowerShell has an excellent help system. Explore it. Get-Help (or just the the alias help) is your friend. If it's too much work, you can also just type a Cmdlet name followed by -? to get help on it. For example, Get-Service -?.

If you want more extensive help with explanations of each parameter and lots of examples, add the -detailed switch:

  help Get-Service -detailed

Beyond Cmdlet help, PowerShell also provides meta help about different topics. To find out which topics are available, type:

  help about_

and PowerShell will display all the topics. Then you can just pick one, such as 'History' and type: help about_history.

A Developer's PowerShell Profile

I'm not a system administrator and I have a strong aversion to long pipelines of commands with convoluted parameters. If you ask me how to find a service that was installed at-most three days ago and its name is longer than 10 characters, I'll have to look it up and do some trial and error until I get it right. I use PowerShell just like I use Bash on Mac OS X and Linux. I define a bunch of functions and aliases in my profile and I rely on them for my command-line needs. I rarely type commands with parameters. This saves me some typing, but more importantly, it saves me from the mental burden of keeping most of the complexity in my head.

Navigation

I almost always work in deeply nested folder hierarchies. I hate typing long path names on the command-line, and even tab completion can take too long if you have many sibling sub-directories — all with the same prefix. I also regularly visit a relatively small set of locations. So my standard practice, if I want to run a command that accepts a path, is to navigate to this location and run "<cmd> .". To be able to navigate quickly to various locations, I define little functions that consist of a series of 'cd' statements:

function cdr { cd 'c:\r' }
function cdp { cdr; cd ProjectCool }
function cdb { cdr; cd Branches }
function cdt { cdr; cd Testing }

Locations and programs

There are several common locations I use multiple times in the profile or full paths to various programs:

$program_files = 'C:\Program Files (x86)\'
$editor = $program_files + 'Notepad++\notepad++.exe'
$rabbitmq_sbin = $program_files + "RabbitMQ Server\rabbitmq_server-2.8.2\sbin\"
$rabbitmq_ctl = $rabbitmq_sbin + 'rabbitmqctl.bat'
$rabbitmq_server = $rabbitmq_sbin + 'rabbitmq-server.bat'
$rabbitmq_service = $rabbitmq_sbin + 'rabbitmq-service.bat'
$dot_net_framework = 'C:\Windows\Microsoft.NET\Framework\v4.0.30319\'
$install_util = $dot_net_framework + "installutil.exe"
$root_dir = 'c:\root'


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 

Comments:

dblake950
2013-04-04T23:42:07

The glitch in the Prompt function code has been fixed.


Permalink
ubm_techweb_disqus_sso_-d282dffe76fccdf5d72ad71339e97ea4
2013-03-27T16:06:36

Kirk, thanks for the info. This is very useful. It bothered me a lot that I have to be so explicit, but I never found out how to do it more concisely.


Permalink
ubm_techweb_disqus_sso_-ccf8c0271edeeb126b4d828d75412114
2013-03-22T00:39:04

Great article!

I completely agree about the benefits of PowerShell being often overlooked by developers.

I think there are a few things you could do to make your life even easier though.

First, when you don't need to use specific arguments in a command-line utility that you want to invoke more easily, use New-Alias instead of creating a function. For example, let's say you wanted to create a shortcut for cmd.exe (assuming it wasn't accessible via your path already), and you wanted to call it dos. You could do that like this:

New-Alias dos (gcm cmd)

Once you have that alias set up, you can invoke dos from anywhere and all arguments you give it will go directly to cmd.exe, without the need to create functions with so many args[x].

Second, when you do want to invoke a command-line utility with a few specific arguments and others that are passed through by the caller, you should use splatting to pass through the other parameters. For example, let's say you wanted to create a wrapper for cmd.exe called dos and remove the need to invoke it with /c all the time. You could do that like this:

function dos {
cmd /c @args
}

Then you can use cmd.exe command from PowerShell like this:

dos compact /?

The key point here is that there are much easier ways to pass arguments through to command-line programs, and leveraging both aliases and functions to do so is a great way to get the best of both worlds.

Thanks for sharing your perspective on the value that PowerShell brings to the table.

Kirk out.


Permalink

Video