Recently in one of my classes, we began using Microsoft’s command line shell and scripting language, Powershell. I’ve been exposed to Powershell several times in the past but have never really had the time to truly explore it until now.

Background

For the uninitiated, Powershell is an object oriented command line shell and scripting environment created by Microsoft geared towards system administrators and power users. Before Powershell was introduced, system administrators were forced to either use VBScript or the primitive Windows Command Prompt.The Windows Command Prompt descended directly from the 16-bit command line shell of Microsoft DOS and is fairly primitive. MS-DOS’s command line shell has lagged behind other command line shells since its conception in 1981. UNIX’s Bourne shell (Which is the basis of the modern day Bash shell and the POSIX command line shell specifications) which was introduced in 1977, functioned as both a command line shell and as a full fledged scripting language. The Bourne shell’s scripting language was advanced enough that it could even compete with several programming languages of the day. In comparison, MS-DOS was capable of executing scripts in the form of batch files. This scripting language however was very primitive, only containing basic control flow structures (If, goto and for statement). When Microsoft went 32-bit and abandoned MS-DOS their 32-bit command interpreter (CMD.exe) for Windows seemed to be based directly off of MS-DOS’s command line interpreter. In fact, very little new features were added. The majority of new features were simply new utilities that came with Windows. These were all just new executables that could be invoked from the command line. System administrators were forced to either use batch files for scripting, or the newly introduced Windows Script Host. Windows 98 introduced a new scripting environment called Windows Script Host (Or WSH). WSH essentially provided a wide range of functions that could be used by any supported scripting language. WSH could be combined with several new languages that Microsoft introduced as part of their new ActiveX technologies. This included VisualBasic Script (VBS, or VBScript) and a dialect of Javascript called JScript. WSH allowed scripts written in these languages to perform administration tasks, such as interacting with Microsoft’s Active Directory Service and running WMI queries (Windows Management Instrumentation). Windows system administrators automated tasks in the form of batch scripts and resorted to WSH when the command prompt wasn’t good enough. Still, both of these technologies paled in comparison to the various administration utilities found on UNIX-like systems. The Bourne Again Shell (Bash) and the Perl programming language (Which I personally despise, however that’s a discussion another day) were both superior to both the Windows Command Prompt and Windows Script Host. Microsoft eventually caught on the short comings of the Windows Command Prompt and Windows Script Host and decided to make an effort to develop a new technology that was comparable with the various administration tools found on UNIX-like systems. This technology was originally called Monad (Also known as Microsoft Shell). A beta version of Monad was released in 2005. The next year, Monad was renamed to Microsoft Powershell. Powershell was designed to provide various administrative tools and would be significantly different from the Windows Command Prompt. One of the things that made UNIX successful was the ability to chain programs together via pipes. In the Bourne shell (And its predecessor, the Thompson Shell) you could redirect the output of one program into the input of another program using the pipe character (“|”). You could also redirect the output of one program into a file using “>”or “»”. These features were mimicked by Microsoft DOS and the Windows Command Prompt and eventually became an expected feature on pretty much any command line shell. At the heart of any UNIX system, there are various command line tools that all serve a very specific purpose. Together, these tools can be chained together to perform very complex operations. For example, lets say that I want a list of all files in /bin. Simple enough? On most UNIX systems, the command ls will do. Now, lets say that I want to sort these files in alphabetical order? Simple, all I’d have to do is redirect the output of ls into sort. At the command line, I’d just enter ls | sort Now lets say we only want to display files that were created after a certain date. How could I do this? Well, there is a tool called find that can do this; but lets just stick to the example with ls. We’d have to write some script that would parse the output from ls -l (Which includes time stamps) and parses each date and matches against the date we want. Complex? Yeah, kind of but UNIX provides several utilities that aid in parsing text. Most UNIX systems come packaged with the AWK scripting language, a language designed specifically for text processing. Another utility found any virtually any UNIX system is Perl, a general purpose programming language that was also designed for text processing, however can be used for just about anything. Both of these could be used to parse the output of ls -l, but isn’t there an easier way to query the output of ls -l? Sadly, there is not as ls -l outputs plain text, which is useful to any literate human however not so useful to a computer. A computer requires something more abstract. Powershell, like the Bourne shell and the Windows Command Prompt supports piping. However, it does not pipe the textual output of a program. Powershell programs (Called “cmdlets”) accept and receive input and output in the form of objects. Objects are far more abstract than plain text and can easily be worked with within Powershell. Lets reconsider the previous example, how could I get all files in a directory, sort them, and only list files that were created by a specific date? Simple, run the Get-ChildItems cmdlet (The equivalent of ls), pipe that into Sort-Object then pipe the output of that into Where-Object which can used to query an array of objects. The resulting command would like something like Get-ChildItems | Sort-Object | Where-Object {$_.CreationTime -gt “mm/dd/yyyy”} No need to parse any text!

The Good

As a C# programmer familiar with the various APIs the .NET framework has to offer, I instantly fell in love with Powershell’s object system. In Powershell, I could load any program or class library written in C# (Or any .NET language, such as Visual Basic or F#) and use is it in my Powershell scripts. I could also load any other .NET assembly such as System.Windows.Forms, allowing to me make graphical scripts utilizing Windows Forms. When I was exploring Powershell in clas,s one of the first things I wondered if I could load and use Linq (Language Integrated Query, a C# Library enabling the programmer to query collections of objects) so I could easily query the output of Powershell commands. I soon learned that Microsoft had already implemented several command line friendly versions of common functions provided by Linq in the form several cmdlets including Where-Object, Reduce-Object, Select-Object, and Foreach-Object. Powershell also has many of the structures any programmer would expect in a scripting language, including if/else statements, for statements and foreach statements. The syntax for all of these statements would be familiar to anyone with any experience in any C like language. Code can be organized in functions, and Powershell also allows the creation of special “filter” functions which allow you to process input from the pipeline.

The Bad

At first, I thought Powershell was the best thing in the world. However, there are several things aspects of Powershell that I personally despise. I’ll briefly describe several of the things that would immediately turn me off from writing any scripts in Powershell. Keep in mind that all of my criticisms stem from my perspective as a programmer, not a system administrator. Function Declaration Syntax When I was exploring the capabilities of Powershell, one of the first things I attempted was to write a function. In my script I wrote

1
2
3
4
Say-Hello();
function Say-Hello() {
    Write-Host "Hello, World!";
}

Upon trying to run this, I was greeted with a syntax error “An expression was expected after (“. I very surprised and disappointed that Powershell doesn’t support the traditional f(x) notation for calling and declaring functions. One of the things I thought Powershell excelled at was consistency, however, here lies one of the most inconsistent aspects of Powershell. In Powershell you must use the traditional f(x) syntax to call .NET functions, however, you don’t even have the option to do this with user defined functions. For example, if I wanted to invoke a user defined function named Write-Line, the syntax would look like Write-Line Hello, World!, however, if I wanted to invoke the .NET WriteLine method found in the Console class, the syntax would be [System.Console]::WriteLine(“Hello, World!”). Anyways, so I changed my Powershell script to:

1
2
3
4
Say-Hello;
function Say-Hello {
    Write-Host "Hello, World!";
}

When I ran the script I was greeted with another error. “The cmdlet or function “Say-Hello” does not exist”. What is this? C? Must we really declare functions before we can use them? In the C and C++ programming language, functions must be either defined or “prototyped” before they can be used. Most newer languages, however, allow a function to be used before or after it was declared. Perl, Python, and Ruby (All suitable alternatives to Powershell scripting) do not require functions be defined before they used.

Dynamic Scoping

Just about every single programming language I have ever used besides Emacs-Lisp and Perl (Which uses static by default, however it is possible to declare a dynamically scoped variable) use static scoping. For those who are confused what the different between dynamic and static scoping is, static scoping essentially means that each function has its own set of variables and can not access variables from functions that called it. Consider the following example in Python:

1
2
3
4
5
6
def f1():
    print x
    
def f2():
    x = 3
    f1()

If you ran this code, you would get an error saying the variable x does not exist, as it was defined in f2’s scope. This is because Python uses static scoping. If Python used dynamic scoping than this example would run perfectly and print “3”. The obvious problem with dynamic scoping in a language like Powershell, where variables are not explicitly declared is that you could accidentally overwrite another function’s variable by mistake. For example, in the previous example if you wanted to define a variable named x in f1 and if Python used dynamic scoping, assigning x in f1 would not create a new variable named x. Instead, it would overwrite f2’s x variable. Powershell attempts to address this problem by using a sort of copy-on-write scoping system where if you overwrite a variable from a previous scope, a new variable is created in the current scope. This makes sense and I could partially support this if it didn’t apply to the global scope as well. For example, lets consider the following Powershell code

1
2
3
4
5
6
7
8
9
$foo = 3;

function Change-Foo($newFoo) {
    $foo = $newFoo;
}


Change-Foo 10;
Write-Host $foo;

What would this code print? “10”? No, it would print “3” as the variable foo exists in the global scope. When you attempt to modify foo from Change-Foo, a new foo is created inside Change-Foo’s scope. This foo is different than the foo defined in the global scope and only exists inside Change-Foo’s scope. Of course, there is a way to get the above to example to work by explicitly making foo global, however this is quite verbose and quite honestly, counter-intuitive. The following example will work as expected and print “10”.

1
2
3
4
5
6
7
8
$global:foo = 3;

function Change-Foo($newFoo) {
    $global:foo = $newFoo;
}

Change-Foo 10;
Write-Host $foo;

Performance

Overall I’ve noticed that Powershell is significantly slower than just about every viable alternative. On just about every system I’ve used Powershell on, there is a significant delay between opening Powershell and getting the “PS” prompt. I’ve also noticed that commands occasionally take anywhere from a few hundred milliseconds to a few seconds to execute. I personally believe Powershell is slow for two reasons; Its usage of the .NET framework (Which I praised earlier) and its object system. At the core of the .NET framework is the Common Language Runtime (CLR) consists of a process level virtual machine. The .NET framework and the Common Language Runtime were both Microsoft’s response to Sun Microsystem’s (Now Oracle) Java programming language and runtime environment. The CLR theoretically allows programs to be written once and on any system with a CLR implementation. This works because programming language targeted on the .NET framework are not compiled into executable machine code. Instead, compilers translate source code into an abstract machine language called the Common Intermediate Language or CIL. When a .NET program is loaded by the CLR, it must convert the program’s CIL instructions into valid machine instructions in a process called Just In-time (JIT) Compilation. Once a .NET program is compiled by the CLR’s JIT compiler, it runs just as fast as it would if it was compiled to run on the target machine’s processor. However, the moments before the program is actually executed, the JIT compiler is hard at work translating the CIL code to machine code and optimizing the program. This, could be one of the reasons why Powershell is so slow. Powershell’s object system could also contribute to its slowness. On a traditional shell, when a program is ran, it takes over the shell and has direct control over console. Powershell cmdlets mostly run in the background however, and Powershell has to wait for the cmdlet to return a collection of objects before it can display anything to the end user. This, coupled with the JIT compilation can both slow down Powershell significantly.

The Ugly

Sigils

In Unix shell’s, it was common to prefix variables with a “$” (This variable prefix is called a sigil). Sigils make sense in the context of shells, as its common to type strings that are not delimited by quotes. With this in mind, its easy to interpolate variables by prefixing an identifier with a sigil. However, I personally find sigils ugly and the only justification for their use is with string interpolation. Most UNIX shell’s only require variable’s to be prefixed with a sigil when interpolating the variable into a string. For example, in Bash if I were to declare a variable named foo I’d simply type foo=”Hello, World”. If I wanted to use foo in a command however, I’d prefix foo with a sigil. For example, if I wanted to print foo I’d type echo $foo. In Powershell however, the sigil is mandatory, even when declaring a variable. My guess is that Microsoft choose to go this route to maintain consistency, however I find the sigil annoying.

Operators

Another thing that I find significantly annoying are Powershell’s boolean operators. Instead of !, ==, !=, >, and <, we have -not, -eq, -ne, -gt, and -lt. The reason why Microsoft did this is apparent, its a command line shell and Microsoft would like to preserve the |, >,, < and » operators for I/O redirection and piping. Still, the .NET framework is fully capable of operator overloading.

Verdict

Powershell is indeed a powerful tool for system administration, and is significantly better than both the Windows Command Prompt and Windows Script Host. I find it very easy to use and love its .NET integration. Despite this, Powershell has several quirks that would irritate any programmer. If I was a Windows system administrator, I’d much rather use Python, Ruby, or even my own toy language Iodine writing complicated scripts. Realistically though, I’m sure I’d end up using Powershell simply because it interops much better with the Windows operating system than any of the other languages I listed. Don’t get me wrong though, Powershell is an incredibly awesome told for system administrators and I’d recommend any system administrator use it (Assuming they don’t have a background in programming). Still, I wouldn’t recommend anyone writing large scripts to use Powershell unless its absolutely necessary. Powershell still seems very domain specific and outside automating Window administration tasks, I’d classify it as a toy language. It is definitely not a general purpose programming language like Python or Perl, and while powerful, I believe Microsoft should develop an alternative for system scripting.