Comparing Windows PowerShell and Bash

Several years have passed since my previous article looking at the command-line languages under Windows and Linux [1] – time to reinvestigate how these two opponents have developed over the past half decade. A comparison of Windows PowerShell and Bash might indicate who has the current edge in modern scripting.

Windows PowerShell was a completely fresh start of Microsoft's command language under Vista (see the "New Beginning" box). Meanwhile, Bash under Linux was always at the core of the system (see the "Tried and Tested" box) with the project participants continuously updating the interface. So, has Bash matured and got better with age, or has inherited too much cruft that weighs it down?

Tried and Tested

Bash development is rather straightforward in comparison. As many as 111 enhancements have been spliced into Bash [2] since my last evaluation in 2007 – the largest number in the jump from version 3.2 to 4.0 in 2009. The comparatively minor changes in the current version 4.2 prove how well Bash has matured.

Just under half (18 of 39) of the built-in Bash functions are from the Bourne Shell. The cd and test are certainly among the most commonly used, but they also count among the relics. The other half of the functions, among them echo and read, came along with the Bourne Again Shell. They handle everyday tasks, with Bash leaving the special ones to other programs.

Splitting the workload has always been a well-known Linux concept. The login shell serves more as a central command function for the many specialized programs. Thus, nearly 100 programs are available in the GNU Coreutils, which comes with just about every Linux distribution [3].

New Beginning

Microsoft has wanted that the graphical interface to be all you need to operate a computer. There was simply no room for an unadorned shell. Hence, Windows command line sat for decades in a hidden niche.

Despite this decision, the system still relied on scripts for management and, in consequence, many interpreter languages soon sprouted up – Windows Scripting Host (WSH), the Windows Management Interface (WMI), HTML Applications (HTA), the Active Directory Services Interface (ADSI), and Visual Basic Script (VBS).

Microsoft then set the system on a totally new course with Windows Vista. Desktop and server editions began sharing the same code base. Developers also reworked the system architecture and based everything on reusable objects. The .NET framework was supposed to be the unifying structure for all modern applications and provide the building blocks for all new ones. An interpreter should be able to access the system library.

The new shell should, therefore, have all the qualities that modern interpreters such as Python and Ruby already had, except now on Windows as well. Hence PowerShell is object-oriented. It can pass the output from one command in a pipe to another command, and .NET components can be integrated. In terms of modern scripting languages, this was hardly a breakthrough, but it proved to be a big step for Microsoft toward professional system administration.

Four generations of PowerShell have emerged from the company since 2006. Windows 7 includes PowerShell 2.0, and Windows 8 has PowerShell 3.0 with many enhancements. The current version 4.0 was written for Windows 8.1 and, with .NET Framework 4.5.1, also comes as an update for Windows 7 and Windows 8.

Comparing Concepts

One of the most important characteristics of a scripting language is the ability to link two commands, assuming that one command receives, understands, and processes the output of the other. As a rule, Bash uses plain text as input and output. Thus, the following command shows the size and name of the three largest files in the /var/log directory:

$ ls -l /var/log | sed 's/ \+/,/g' | cut -d',' -f 5,9 | sort -g | tail -3
267453,core.log
443742,core.log.1
584584,lastlog

In Windows PowerShell, the input and output are objects; thus, entire data structures migrate from command to command, which assumes that objects follow certain guidelines. To get the same result in Windows as in the previous example, you need to know the properties of the corresponding objects:

PS> Get-ChildItem -Directory | Sort-Object -Property Length | Select-Object Length,Name -Last 3
   Length Name
   ------ ----
 597379 Manual.pdf
 5715026 Applications.in.Ruby.pdf
 27318109 AnnualCatalogue_de-EU.pdf

The fact that Bash presents output as text naturally makes it easier to pass information on to the next program. Most things can be worked out by trial and error – which is nearly impossible in PowerShell.

In the example, you need the properties Length and Name. Intuitively, you might have used Size instead of Length. So, to know the properties of an object, sometimes the documentation is the only resort.

As it is, the example from PowerShell 3.0 is already easier than in previous versions. In earlier versions, the Get-ChildItem cmdlet (which corresponds roughly to ls) didn't yet recognize the -Directory option. Instead, it depended on a subsequent cmdlet (Where-Object {-not $_.PSIsContainer} ) to be built into the pipe to filter by directory, because only directories have the property PSIsContainer.

The difference in handling input and output is just one of many differences between Bash and PowerShell, but it's strikingly obvious. The difference in concept is basically that Bash aims at division of labor while PowerShell focuses on tasks, which was reinforced further in versions 3.0 and 4.0.

Steep Learning Curve

Bash uses only about 40 internal functions and 100 helper programs. PowerShell 4.0 has 299 built-in cmdlets. Version 2.0 (Windows 7) already had 251. Every cmdlet has options, which increase in number with each new PowerShell version.

Thus, the Get-Content cmdlet in Version 3.0 (Windows 8) has the option -Tail <count> , which the developers apparently borrowed from the Unix tail command. Microsoft provides a quick reference guide to the new PowerShell features [4].

The cmdlets are more like mini-programs compared to the Bash built-ins, which becomes obvious when the cmdlet does not do what you expect it to do. When this happens, which is often, you'll need to look at the reference document to find the correct parameters and options. On the plus side, cmdlets can execute more complex tasks in one call. For example, to call up all the links on the ubuntu-user.com website, all you need in PowerShell 3.0 are the commands in Listing 1.

Listing 1

Call Up Links with PowerShell

PS> (Invoke-WebRequest http://www.ubuntu-user.com/).links
innerHTML : <IMG border=0 alt=logoOL.gif src="http://www.ubuntu-user.com/pix/logoOL.gif" width=140 height=62>
innerText :
outerHTML : <A href="/"><IMG border=0 alt=logoOL.gif src="http://www.ubuntu-user.com/pix/logoOL.gif" width=140 height=62></A>
outerText :
tagName   : A
href      : /

Sometimes this task orientation seems a bit much, and it makes certain tasks a bit cumbersome. To generate a password in Bash, all you need is to read the file descriptor for the random number generator and prepare it somewhat (see Listing 2).

Listing 2

Generate a Password with Bash

$  echo $(< /dev/urandom tr -dc _A-Z-a-z-0-9 | head -c10)
Kc-TUGC8cd

To accomplish the same in PowerShell 3.0, you can load a system library from .NET Framework and use a module from it. In the Assembly System.Web, you'll find the Security.Membership module and its GeneratePassword function (Listing 3). An assembly is similar to the Shared Object principle in Linux. You can load assemblies into scripts and use their functions from there.

Listing 3

Generate a Password with PowerShell

PS> $Assembly = Add-Type -AssemblyName System.Web
PS> [System.Web.Security.Membership]::GeneratePassword(10,3)
lGv%.ua]Wl

PowerShell may leave you wondering things like how to determine which objects use which methods, and in which of the many thousand system libraries can you find the function GeneratePassword. PowerShell definitely requires some heavy lifting. You need to study the extensive reference to the .NET Framework [5] to get anywhere.

Assistance

To get command help in Bash, you can just enter apropos <keyword> , which lists all commands that include the given keyword. To find out which options a command supports, simply enter the command without options. To get to know Bash even better, a look at the Bash Reference also helps [6].

There is indeed a Get-Help cmdlet in PowerShell, but it doesn't help beginners in dealing with slightly more complex objects. Microsoft therefore provides a small development environment for PowerShell that includes an interactive overview of the commands and lets you explore the cmdlet properties to be able to test, activate, or configure them (Figure 1).

Figure 1: The PowerShell ISE development environment is part of the PowerShell installation.

The PowerShell Integrated Scripting Environment (ISE) proves to be quite useful in that it not only makes working with cmdlets easier, but it also includes a debugger that executes the scripts step by step to help in troubleshooting. You can also use ISE to start a PowerShell on a remote computer, similar to SSH in Linux. For this to work, the administrator needs to allow remote access emitting the Enable-PSRemoting -Force command, which evokes the WinRM service to provide the access.

The question of a development environment doesn't even come up for many Linux admins in that powerful editors such as Vim or Emacs have many functions that facilitate scripting, such as syntax highlighting, complex functions to navigate scripts, and built-in macros. Editors of this kind have not as yet become standard Windows features.

Conclusion

The examples I have given show how different the shell concepts are. Aliases such as ls, mkdir and ps might make the changeover of admins from Unix to Windows easier, but it's doubtful that this will make any difference in the end.

PowerShell shows its strengths mainly in administering Microsoft server applications. The cmdlets are designed to execute tasks with a minimum of commands. The reason entire objects are used to communicate instead of plain text is because of the underlying framework more than to simplify administration.

Bash takes a completely different approach. Shell scripts don't manage products but rather a versatility of tools. For more complex tasks, you can use specialized interpreters such as Perl and Ruby. The main purpose of Bash is to manage systems.