BUY THIS BOOK
Add to Cart

Print Book $34.95


Add to Cart

Print+PDF $45.44

Add to Cart

PDF $27.99

Safari Books Online

What is this?

Add to UK Cart

Print Book £24.95

What is this?

Looking to Reprint or License this content?


Monad (AKA PowerShell)
Monad (AKA PowerShell) Introducing the MSH Command Shell and Language By Andy Oakley
December 2005
Pages: 206

Cover | Table of Contents | Colophon


Table of Contents

Chapter 1: Introducing MSH
Monad, also known more formally as the MSH Command Shell, is a next generation Windows command shell. Built on top of the .NET Framework, MSH provides a powerful infrastructure for the automation of a wide range of administrative tasks. At last, the command line is a first-class citizen in the world of Windows system management.
It would be unfair to characterize MSH as simply an evolution of the cmd.exe shell, a system whose roots reach back to the days of MS-DOS and before. Indeed, although the standard "host" of MSH is a console application, MSH is designed so that it can be used in other contexts, such as the MMC (Microsoft Management Console). This new shell is built from the ground up with a focus on structured data and today's administrative challenges.
The pipeline, a mechanism for passing data between different functional units, has long been a feature of many shells, including cmd.exe. MSH goes beyond the traditional notion of using text to pass data between the different stages of the pipeline and all of the "prayer-based parsing" that goes with it by allowing the transfer of structured data in the form of .NET objects between the pipeline elements. This self-describing information can be used at any point in a complex sequence and allows any process to operate on data in an intelligent fashion, even pipeline elements that have never seen a given type of data before.
MSH also uses a provider model so that the many types of hierarchical data stores used with Windows systems can be accessed through a single consistent set of commands. In MSH, the mechanism for retrieving folders, files, content, and the current location applies not only to filesystems but also to other stores, such as the registry.
Repeatability and consistency are the two words that capture some of the real value of using MSH. Administering a single machine today is a simple task; Windows offers a well-organized graphical interface for settings and configuration, and Terminal Services makes it easy to effect changes on a server located on the other side of the world. Unfortunately, in simple terms, this model doesn't scale well at all; it takes twice as long to manage 2 machines and 10 times as long for 10. Fortunately, there are management tools such as Systems Management Server (SMS) that ease this burden across the enterprise. Also, technologies such as WSH (Windows Scripting Host) and Perl can be used to automate repetitive tasks. By offering a scriptable language, MSH offers yet another alternative to the manual click-by-click sequence where a one-time investment in authoring a configuration script enables quick replay on a potentially large number of machines with predictable results. Add this all up, and there's a lot of time to be saved.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Get MSH
MSH is supported on a number of currently available Microsoft operating systems. To use MSH, you'll need to be running on one of the following platforms:
  • Microsoft Windows XP SP2
  • Microsoft Windows Server 2003 SP1
  • Microsoft Windows Vista (formerly Windows Code Name "Longhorn")
In addition to a supported operating system, MSH requires the .NET Framework 2.0 redistributable, SDK, or Visual Studio 2005.
Everything needed to get up and running can be downloaded from the Web. Follow these installation steps and you'll be ready to go:
  1. Download and install the .NET Framework 2.0 Redistributable from http://msdn.microsoft.com/netframework/downloads/updates/default.aspx. Several versions are available for different machine architectures (32-bit and 64-bit); pick the one suitable for your machine.
  2. Go tohttp://download.microsoft.com and search for "Monad". Download and install the latest release.
That's it. Let's get started!
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Get to Know Verb-Noun Syntax and Cmdlets
We'll begin by getting the shell up and running so that we can start to put MSH through its paces. This section will focus on the time-honored task of inspecting the process list to see what's currently running on a system. There are some MSH features that may not be immediately familiar to those in other command shells—in particular, the strict command syntax—but we'll also look at a few of the obvious differences and see how they're really nothing to fear.
These basics show you how to start using the shell, and they provide the foundation for the rest of the examples we'll cover.
Most MSH commands are identified by a pair of words, one verb and one noun, separated by a hyphen. The verb describes the action (such as get or set) while the noun represents the target of the action in singular form (such as process or location). There is a standard list of verbs that covers the majority of tasks (including get, set, add, and remove). Although the number of these verbs may seem excessively large, consistent naming does make learning and using MSH easier in the long run.
Let's begin by starting the shell. From the Start menu, select Run and type MSH. You'll see a console window open up with a small introduction:
    Microsoft Command Shell
    Copyright (C) 2005 Microsoft Corporation. All rights reserved.

    MSH D:\MshScripts>
MSH is waiting for its first command. The MSH shell is in interactive mode with the current directory set to D:\MshScripts. We'll look at the different modes of operation in more detail later on. For now, the shell will execute commands line-by-line as they are entered.

Section 1.2.1.1: Running a first command

Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Access the Registry Like a Filesystem
Historically, command-line shells have been intimately tied to the filesystem. With a well-defined hierarchical structure, commands are provided to move up, down, and around; work on items at some location within; or even to extend or contract the structure. Many years have shown a hierarchy is an effective representation of a data store, even if it does introduce a few problems—for example when trying to manipulate a set of items scattered in different locations throughout the tree.
Of course, it hasn't always been possible to look at a filesystem in the single consolidated manner that we enjoy today. Even with the variety of stores and protocols now in use—from FAT, NTFS, and CIFS to ISO9660—modern operating systems abstract the differences away from us, leaving a single, simple view of hierarchical folders and files. MSH takes this concept a step further and embraces other hierarchical data stores, such as the registry, to enable us to navigate around them and act within them as if they were a simple folder structure on disk. As we're about to see, this makes many tasks much easier.
In MSH, a provider forms the abstraction layer that exposes a hierarchical store to the shell as another drive. In addition to the familiar A, C, and D drives, you can now see drives representing environment variables, MSH functions, shortcuts to My Documents, and parts of the registry. This list isn't fixed and drives can be added, each backed by a different provider.
Let's take a look at the simple example of Windows Notepad and how to change its configuration via the registry.
The registry is divided into five primary divisions known as hives , two of which we'll look at here.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Create a Pipeline to Pass Information
The cmd.exe command shell has always supported the idea of redirecting the output of a process to a different location. The vertical bar, or pipe symbol (|), is used to create this pipeline. When present, it instructs the shell to redirect the output of one command to the input stream of another, effectively chaining the commands together. For example, while the command type win.ini | more is a familiar way of paging through a long text file, what's really happening is that the output of the type command, which lists the contents of a file in its entirety, is being piped to the more command, which knows how big the screen is and how to pause when it's full.
The pipeline is not a new concept and is the glue that most command shells use for passing data between different processes. However, MSH takes the concept one step further. Instead of passing simple text streams between different steps (the method used by almost all command shells today), for communication between MSH cmdlets and scripts, strongly typed objects are used to carry both the information and its structure.
Passing strongly typed data has some significant advantages. Flat text files are rarely the best way to represent structured data as there is only so much information that can be captured in a line-to-line text listing. Historically, when information is transferred between two processes in textual format, there will be some mutually agreed upon encoding—maybe the generator will always output information in some sequence that the receiver must then parse to recreate the structure for processing. Authors must either add complexity to their tool by generating both human-readable and machine-readable output, or they end up forcing the script writer to use a parsing tool such as AWK to extract meaning from the textual output. Such parsers can be difficult to write, are prone to failure with minor tool changes, and cannot always handle international characters. Clearly, this tightly binds the two tools together, requiring code in both to handle the interchange. This is often so restrictive that it limits the tools so that they cannot be used for any other purpose. With MSH passing structured data instead of text, much of this encoding and decoding effort disappears and arbitrary pipelines become a reality.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Display Data
So far, we've been building commands with little attention to the output, other than assuming it will be displayed in the console window. How does MSH know how to neatly display the objects, especially if it's not familiar with their structure? It turns out that there is a fair bit of plumbing in MSH to make sure that console output is displayed in a useful form for a wide range of types.
The default formatter is a standard part of the shell. If and when an object reaches the end of the pipeline, MSH inspects its type and compares it to a list of known objects. If a match is found, MSH understands how to best format the object for display; if not, MSH displays the .NET properties of the object.
The type of the first object to reach the end of the pipeline generally governs how subsequent objects will be displayed. Alternatively, a command or script can explicitly insert formatting cmdlets (from the format-* family) and/or outputting cmdlets (from the out-* family) in the pipeline for more explicit control over how and where objects should be displayed.
Let's take a look at some of the different tools available for presenting data.
Armed with the knowledge that there is a default formatter at work, we can look at the output of
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
What's Next?
By now, you've hopefully got a taste for the power of MSH as a tool. In the chapters that follow, we'll look more deeply into the different facets of the environment, starting with the basics and moving on to talk about the different components in more detail. Welcome aboard!
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Chapter 2: Customizing MSH
Now that we've seen a handful of the features that MSH has to offer, it's time to settle in and become familiar with the new environment. In this chapter, we'll look at some of the practical tasks that will save a lot of time in the future.
All of the examples so far have followed the same procedure: MSH presents a prompt and waits for your input, you type a command, the command is processed, the output is displayed, and the cycle repeats. This is useful for interactive command line use, invoking cmdlets one by one, building pipelines, and general day-to-day use.
However, like cmd.exe, MSH can take its instructions from a file instead of receiving commands from a keyboard. This enables us to create complex sequences that perform one or more tasks and save these scripts to disk. In this way, it's possible to build up a library of scripts that encapsulate common repetitive tasks. With this library on hand, it's easy to save time and quickly recall and rerun previously stored scripts.
MSH scripts can be stored in simple text files with a .msh extension in much the same way as command-shell scripts can be stored in text files with a .bat or .cmd extension. Using your favorite text editor, let's put together a simple script that filters the output of get-process. Save the script, shown in Example 2-1, as get-processHandlesGt500.msh in a convenient directory. (We'll use D:\MshScripts in the examples that follow.)
Example 2-1. get-processHandlesGt500.msh
get-process | where-object { $_.Handles -gt 500 }
Once saved, the script can be run in the shell simply by typing in its path and name. If the script is located in the current working directory, the current path must also be specified as .\get-processHandlesGt500.msh
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Load and Save Scripts
All of the examples so far have followed the same procedure: MSH presents a prompt and waits for your input, you type a command, the command is processed, the output is displayed, and the cycle repeats. This is useful for interactive command line use, invoking cmdlets one by one, building pipelines, and general day-to-day use.
However, like cmd.exe, MSH can take its instructions from a file instead of receiving commands from a keyboard. This enables us to create complex sequences that perform one or more tasks and save these scripts to disk. In this way, it's possible to build up a library of scripts that encapsulate common repetitive tasks. With this library on hand, it's easy to save time and quickly recall and rerun previously stored scripts.
MSH scripts can be stored in simple text files with a .msh extension in much the same way as command-shell scripts can be stored in text files with a .bat or .cmd extension. Using your favorite text editor, let's put together a simple script that filters the output of get-process. Save the script, shown in Example 2-1, as get-processHandlesGt500.msh in a convenient directory. (We'll use D:\MshScripts in the examples that follow.)
Example 2-1. get-processHandlesGt500.msh
get-process | where-object { $_.Handles -gt 500 }
Once saved, the script can be run in the shell simply by typing in its path and name. If the script is located in the current working directory, the current path must also be specified as .\get-processHandlesGt500.msh:
    MSH C:\> D:\MshScripts\get-processHandlesGt500.msh

    Handles  NPM(K)    PM(K)      WS(K) VS(M)   CPU(s)     Id ProcessName
    -------  ------    -----      ----- -----   ------     -- -----------
        788      14    13832      16132    70   202.02   1656  CcmExec
        764      12    36760      34084   175    20.41   1864  msh
       1792      54    22476      24336   106   418.00    824  svchost
        557      60     7352       3820    51    58.92    488  winlogon
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Save Keystrokes with Aliases
Isn't it odd that cd, dir, and echo still work in MSH? These commands don't follow the regular verb-noun format for cmdlets, yet MSH will happily work with them. This is an example of aliases at work. An alias is a mapping from one string to another, usually used to map a short name to any longer name for convenience.
When running the dir command, MSH is silently translating it to get-childitem (the equivalent cmdlet) and running that instead. This feature helps to shorten command length (and keystrokes) and bridge the syntax gap for the transition to MSH.
It's easy to set up your own aliases for commonly used commands.
Let's start by taking a look at the default aliases . With no parameters, the get-alias cmdlet lists all active aliases:
    MSH D:\MshScripts> get-alias


    CommandType     Name                            Definition
    -----------     ----                            ----------
    Alias           ac                              add-content
    Alias           clc                             clear-content
    Alias           cli                             clear-item
    Alias           clp                             clear-property
    Alias           clv                             clear-variable
    Alias           cpi                             copy-item
    Alias           cpp                             copy-property
    Alias           cvpa                            convert-path
    Alias           epal                            export-alias
    Alias           epcsv                           export-csv
    Alias           fc                              format-custom
    Alias           fl                              format-list
    Alias           foreach                         foreach-object
    Alias           ft                              format-table
    Alias           fw                              format-wide
    Alias           gal                             get-
    ...
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Work with the Command Line
Because cmd.exe runs in the Windows console subsystem, it inherits a number of features useful for command-line editing . The ability to edit the current command, as well as recall previous ones, proves to be a useful tool when working in an interactive command shell (and this feature continues to work in MSH). In this short section, we'll look at some of the standard editing keystrokes, as well as changes in the history buffer, that MSH offers.
Command-line editing makes life a lot easier when entering commands in interactive mode. Suppose you've missed a parameter; no problem, just use the left arrow key to move back to the correct position and insert it. Need to fix a typo? Same idea. Use the cursor key to move to the correct position and then use Delete or Backspace to make any necessary changes. Other familiar shortcuts, such as Ctrl-left arrow, Ctrl-right arrow, Home, and End, will skip between words and go to the start and end of the command line, respectively.
When in interactive mode, every command entered is stored in what is known as the history buffer. The idea of a history buffer isn't new and is available in most command-line interpreters available today. As with most shells, the up and down arrows can be used to navigate through the history buffer, recalling previously executed commands and bringing them to the current command line. Also present is the Page Up/Page Down mapping to recall the first and last entries in the history buffer, respectively. Function keys also play their part, from F3 copying the remainder of the previous command into the current command line to F7 displaying a list of all history items in a pop-up box. Try them out, and you'll find that all of the command-prompt keystrokes continue to behave as expected.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Make Yourself at Home
For anyone who has spent significant time working in a shell, one thing quickly becomes apparent: there are some tasks that you repeat continuously until you finally break down and automate them with a script, often to be run at startup. Customizing your environment and creating your own shortcuts can significantly reduce the time spent on repetitive tasks and broaden the toolset at hand for solving new problems.
In this section, we'll look at a special script called the profile that can be used to customize the shell. We'll bring together some of the techniques we've already covered, in addition to a couple of new ones, and we'll create a script file that will be run every time the shell is started.
It's time for the text editor again. We'll create a file called profile.msh in the MSH directory of your My Documents folder. We can edit this file as we would any other script, but its name and location have special meaning: when MSH starts up, it will look for this script and run it. For now, let's write a script that configures some aliases, sets up a couple of useful functions, and prints a welcome message, as shown in Example 2-3.
Example 2-3. profile.msh in My Documents\MSH
# my personal aliases
set-alias ms get-member
set-alias prop get-property
set-alias displaytext write-object

# my personal functions
function prompt { "$((get-date).Month)/$((get-date).Day) " }
function day { (get-date).Day }

# increase the history buffer size
$MaximumHistoryCount=1024

# welcome message
"Welcome to MSH, " + $env:Username
That's it. Next time you start the shell, the customizations enacted by
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Find Out What a Command Will Do Before Running It
MSH introduces several so-called ubiquitous parameters that are available throughout the command shell. Three important ones, -Verbose, -WhatIf, and -Confirm, tell you exactly what's going on, tell you what should be going on and check whether you'd like it to be going on (respectively) when running any MSH command. They can be used to get a better picture of how a script will perform before it is run, allowing you to verify the actions before they are performed.
Let's take a look at some of these safeguards in action.
Using the get-childitem cmdlet, it's an easy task to walk through a folder tree and pick out the files that match a certain criteria. Casting caution aside, you could pipe that output into remove-item to delete the matching files. This could remove more files than expected, however, so it's a good idea to use the -WhatIf switch to see what would happen before making any irreparable changes:
    MSH D:\MshScripts> get-childitem . *.bak -Recurse | remove-item -WhatIf
    What if: Operation "Remove File" on Target "test.bak"
    What if: Operation "Remove File" on Target "reports.bak"
    What if: Operation "Remove File" on Target "authors.bak"
    What if: Operation "Remove File" on Target "contracts.bak"
    What if: Operation "Remove File" on Target "accounts.bak"
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
What's Next?
Now that you're settled in and comfortable in these new surroundings, it's time to take a look at some more MSH features, including variables, functions, and language constructs such as conditional tests and loops.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Chapter 3: Scripting MSH
In this chapter, we'll discuss some of the core MSH language features that complement the features we've already covered, as well as how they can be used to build more powerful scripts. As we'll be looking at the basic building blocks, some of the examples may appear rather abstract. However, we'll keep the examples short and simple to help introduce the behavior and purpose of these new features. We'll waste no time putting these new constructs to work as we move forward.
Before we begin, let's look at how the .NET Framework fits into the picture.
Although the .NET Framework is invariably described in terms of C# and VB.NET—two programming languages that rely on the infrastructure they provide—it's not just for developers. There are a number of facets to the framework, including a Common Language Runtime (CLR), which provides a common set of functionality to all .NET applications and tools. Compilers for the .NET programming languages generate managed code, which is a set of instructions understood by the CLR. When it comes time to run the managed code, the CLR converts it into native executable instructions (suitable for the architecture of the machine) and actually runs it. This whole process is largely transparent to both the developer and the end user. To complement the runtime, there is a broad Class Library that contains all kinds of useful functions and data types.
Although MSH itself is written as managed code, the only important takeaway is that the .NET Framework redistributable is needed for any MSH work. However, for our purposes, there are two key aspects of the .NET Framework that we'll see in MSH time and again: strongly typed structured objects and the Class Library.
Most command-line administrative shells, such as cmd.exe, have just one or two data types: strings and integers. Creative use of strings and integers has resulted in some really clever scripts, but interoperation and portability between different processes is often bound by a strict contract such as the exact format of string. The .NET Framework defines not only a number of standard data types, including strings and integers, but also Booleans (true/false), floating point numbers, and arrays (an ordered collection of many items of the same type). We call variables that are declared with these types
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
The .NET Framework
Although the .NET Framework is invariably described in terms of C# and VB.NET—two programming languages that rely on the infrastructure they provide—it's not just for developers. There are a number of facets to the framework, including a Common Language Runtime (CLR), which provides a common set of functionality to all .NET applications and tools. Compilers for the .NET programming languages generate managed code, which is a set of instructions understood by the CLR. When it comes time to run the managed code, the CLR converts it into native executable instructions (suitable for the architecture of the machine) and actually runs it. This whole process is largely transparent to both the developer and the end user. To complement the runtime, there is a broad Class Library that contains all kinds of useful functions and data types.
Although MSH itself is written as managed code, the only important takeaway is that the .NET Framework redistributable is needed for any MSH work. However, for our purposes, there are two key aspects of the .NET Framework that we'll see in MSH time and again: strongly typed structured objects and the Class Library.
Most command-line administrative shells, such as cmd.exe, have just one or two data types: strings and integers. Creative use of strings and integers has resulted in some really clever scripts, but interoperation and portability between different processes is often bound by a strict contract such as the exact format of string. The .NET Framework defines not only a number of standard data types, including strings and integers, but also Booleans (true/false), floating point numbers, and arrays (an ordered collection of many items of the same type). We call variables that are declared with these types strongly typed , meaning that if a variable claims to be a double precision floating-point number, we can be confident that it really is a number and not a string or something else. There's no need to check that the data inside a floating-point number is actually in the correct format because the .NET Framework handles that.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Work with Structured Objects
Just as a DateTime object has properties for year, month, day, and so on, so too do the other types we've already seen. The Process class includes process ID, name, and other information, and the get-process cmdlet generates a sequence of objects, one for each process running on a system, with each of these values set accordingly. It's not immediately obvious from the output of get-process how much information is lurking within these objects. Next, we'll see that there's far more than just process name and ID.
Let's take a more detailed look at what these objects contain and how—with just the few cmdlets we've discussed so far—there's much more information waiting to be tapped.
With all of the information stored inside these objects, we need a guide—something that will help us get more familiar with these new data structures we're working with . The get-member cmdlet is here to make life easier. With no parameters, it lists every detail about the object. For the following examples, we'll be focusing on the properties (the "public" values and settings). Therefore, we'll tell get-member to just return those:
    MSH D:\MshScripts> get-process | get-member -MemberType Property

        TypeName: System.Diagnostics.Process

    Name                       MemberType Definition
    ----                       ---------- ----------
    BasePriority               Property   System.Int32 BasePriority {get;}
    ExitCode                   Property   System.Int32 ExitCode {get;}
    HasExited                  Property   System.Boolean HasExited {get;}
    ExitTime                   Property   System.DateTime ExitTime {get;}
    Handle                     Property   System.IntPtr Handle {get;}
    HandleCount                Property   System.Int32 HandleCount {get;}
    Id                         Property   System.Int32 Id {get;}
    MachineName                Property   System.String MachineName {get;}
    MainWindowHandle          Property   System.IntPtr MainWindowHandle {get;}
    MainWindowTitle           Property   System.String MainWindowTitle {get;}
    MainModule              Property   System.Diagnostics.ProcessModule MainM...
    MaxWorkingSet             Property   System.IntPtr MaxWorkingSet {get;set;}
    MinWorkingSet             Property   System.IntPtr MinWorkingSet {get;set;}
    Modules                 Property   System.Diagnostics.ProcessModuleCollec...
    NonpagedSystemMemorySize  Property System.Int32 NonpagedSystemMemorySize ...
    NonpagedSystemMemorySize64 Property System.Int64 NonpagedSystemMemorySize6...
    PagedMemorySize            Property   System.Int32 PagedMemorySize {get;}
    PagedMemorySize64          Property   System.Int64 PagedMemorySize64 {get;}
    PagedSystemMemorySize   Property   System.Int32 PagedSystemMemorySize {get;}
    PagedSystemMemorySize64  Property  System.Int64 PagedSystemMemorySize64 {...
    PeakPagedMemorySize      Property   System.Int32 PeakPagedMemorySize {get;}
    PeakPagedMemorySize64   Property   System.Int64 PeakPagedMemorySize64 {get;}
    PeakWorkingSet             Property   System.Int32 PeakWorkingSet {get;}
    PeakWorkingSet64           Property   System.Int64 PeakWorkingSet64 {get;}
    PeakVirtualMemorySize   Property   System.Int32 PeakVirtualMemorySize {get;}
    PeakVirtualMemorySize64  Property  System.Int64 PeakVirtualMemorySize64 {...
    PriorityBoostEnabled     Property  System.Boolean PriorityBoostEnabled {g...
    PriorityClass          Property   System.Diagnostics.ProcessPriorityClas...
    PrivateMemorySize          Property   System.Int32 PrivateMemorySize {get;}
    PrivateMemorySize64      Property   System.Int64 PrivateMemorySize64 {get;}
    PrivilegedProcessorTime  Property  System.TimeSpan PrivilegedProcessorTim...
    ProcessName                Property   System.String ProcessName {get;}
    ProcessorAffinity   Property   System.IntPtr ProcessorAffinity {get;s...
    Responding                 Property   System.Boolean Responding {get;}
    SessionId                  Property   System.Int32 SessionId {get;}
    StartInfo           Property   System.Diagnostics.ProcessStartInfo St...
    StartTime                  Property   System.DateTime StartTime {get;}
    SynchronizingObject   Property   System.ComponentModel.ISynchronizeInvo...
    Threads              Property   System.Diagnostics.ProcessThreadCollec...
    TotalProcessorTime    Property   System.TimeSpan TotalProcessorTime {get;}
    UserProcessorTime     Property   System.TimeSpan UserProcessorTime {get;}
    VirtualMemorySize          Property   System.Int32 VirtualMemorySize {get;}
    VirtualMemorySize64     Property   System.Int64 VirtualMemorySize64 {get;}
    EnableRaisingEvents     Property   System.Boolean EnableRaisingEvents {ge...
    StandardInput          Property   System.IO.StreamWriter StandardInput {...
    StandardOutput         Property   System.IO.StreamReader StandardOutput ...
    StandardError          Property   System.IO.StreamReader StandardError {...
    WorkingSet                 Property   System.Int32 WorkingSet {get;}
    WorkingSet64             Property   System.Int64 WorkingSet64 {get;}
    Site                    Property   System.ComponentModel.ISite Site {get;...
    Container               Property   System.ComponentModel.IContainer Conta...
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Store Information in Variables
So far we've been building increasingly complex command-line sequences to solve problems. Even with only the aspects of MSH that we've discussed thus far, it's clear that we've already got quite a toolkit. However, in some cases, it becomes important to maintain some state while processing is underway. Think about making modifications to a subset of thousands of files and needing to keep a record of those that have changed. What if you had a lookup mechanism that could associate a person's full name, email address, and other details with his or her username? This is a scenario in which you might want to take the output of one pipeline and run it into two or more separate ones.
Whether data is needed for decision making during execution or will be used as part of a result or audit after completion, we need somewhere to keep this data for later use. Variables are the mechanism by which MSH allows us to maintain state during processing. Variables are a place to store objects; along with the data they hold, they maintain a sense of the objects' type and structure. Variables can be used to store just about anything, from numbers to filenames to sequences, all the way up to collections of objects.
We'll begin here with a few simple scenarios to become familiar with variable usage and syntax. By the end of the chapter, we'll bring everything together and see how variables, combined with conditional tests and functions, add an extra layer of versatility for solving problems.
Variables in MSH are identified with the $ prefix. This helps separate variables from cmdlets, aliases, filenames, and other identifiers used in the shell. There is no forced naming convention for variables, although it's always good practice to use a name that actually represents the information stored within; if nothing else, it can dramatically help readability and understanding of scripts later on. Variable names are always case-insensitive, and they can contain any combination of alphanumeric characters (
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Control Script Flow with Comparisons
Now that we have all this state to work with, it's time to start making some decisions. We have already touched on how to make tests and comparisons in Chapter 1, and we'll be using the same operators here. In this section, we'll explore the if and switch statements and see how they can be used, depending on certain conditions, to execute different parts of a script.
Starting with some simple cases, let's make comparisons between two constants. This will lay the foundation for what is to come:
    MSH D:\MshScripts> 7 -gt 5
    True

    MSH D:\MshScripts> 7 -eq 5
    False

    MSH D:\MshScripts> "alpha" -lt "zulu"
    True
No surprises here. MSH is performing the comparison and putting a Boolean (true/false) value into the pipeline based on the outcome. Tests become more interesting when we compare a dynamic value against a constant one, but, in every case, a test will always generate a true/false value.
In this case, we'll compare the number of files in a given folder against an arbitrary value considered to be "many," and assign the result to a variable:
    MSH D:\MshScripts> set-location c:\windows\system32
    MSH C:\WINDOWS\system32> $lotsOfFiles = ((get-childitem).Count -gt 50)
    MSH C:\WINDOWS\system32> $lotsOfFiles
    True
The comparison operators provide the framework for determining things about the environment in which the script is running. Naturally, now that we're able to make decisions about state, we can put them into action and control the flow of execution. Our ally here is the if statement and its relations
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Do Repetitive Work with Loops
If there's one thing computers are good at, it's executing the same code over and over again. Looping is a powerful concept that can simplify many repetitive tasks. Using a loop, we can write a single block of script that is executed a multiple number of times based on some defined conditions. This lets us focus on getting the real logic working correctly in a single place and then easily scaling it out or applying it as needed. No longer are we constrained to the copying and pasting command sequences to perform the same actions on different files, folders, machines, or settings. Better still, any changes to the logic made once centrally will then be applied in each iteration the next time the script is run.
In the following examples, we'll look at populating a file structure with some report templates. We'll start by creating a report placeholder for each week and then move on to separating them out month by month.
We'll use a couple of files in the following examples as sample report templates. From the interactive mode, enter the following commands to set up the environment for this example:
    MSH D:\MshScripts> new-item -Name "Reports" -Type Directory
    MSH D:\MshScripts> "Weekly report template" >WeeklyTemplate.txt
    MSH D:\MshScripts> "Monthly report template" >MonthlyTemplate.txt
Now, with an empty Reports directory into which we'll soon place some reports and a couple of simple templates, we're ready to go. Fire up your favorite text editor and create a file called new-weeklyReports.msh with the script in Example 3-4.
Example 3-4. new-weeklyReports.msh
for ($week=1; $week -le 52; $week++)
{
    copy-item "WeeklyTemplate.txt" "Reports\Week $week.txt"
}
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Capture Reusable Behavior in a Function
As we saw earlier, it's easy to start with several simple tasks and join them together to create increasingly complex scripts and pipelines. At some point, instead of using the recall buffer, it is convenient to store command sequences in scripts for later reuse. Functions serve a similar purpose by enabling us to collect one or more commands and group them together so that they may be run with a single command. What sets functions apart is that they can be used to return a result based on the processing of a set of inputs—in effect, the function becomes a black box that encapsulates some frequently used logic.
Several of the built-in commands, such as clear-host, are, in fact, functions that perform tests, determine parameters, and invoke cmdlets based on their inputs. The new-item cmdlet is very flexible, yet at the same time, it can have a cumbersome syntax; thus, MSH creates these short-cut functions for convenience. Even familiar commands such as C: and D: are functions that call the set-location cmdlet.
We'll see how functions offer the means to bring together the various topics we've seen in this chapter, including variables, conditional tests, and loops. Functions can range from the very simple to the surprisingly complex, yet they invariably offer a way to arrange scripts neatly in logical sections, making them much easier to understand and maintain than other methods.
For our first function, let's start with something simple that prints out a welcome message:
    MSH D:\MshScripts> function say-greeting { "Hello" }
    MSH D:\MshScripts> say-greeting
    Hello
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Transform Objects as They Pass Through the Pipeline
Functions remain one of the most versatile aspects of the MSH language. Their use in both command-line and pipeline situations makes them very flexible. However, there are times when we're creating solely for pipeline use, and it's more convenient to run a script block on each object in the pipeline than getting them delivered in one large chunk in the $input variable. Let's say we're going through each line of a 400 MB daily IIS logfile to figure out the set of requested URLs that resulted in a 404 error message. Loading all 400 MB into memory probably isn't the best approach; we would do better to create a small script block that could match a 404 line in the log and record the requested URL into a variable.
To accommodate these cases, MSH offers a special type of function, called a filter, that is designed to be placed into the pipeline and used to inspect, modify, or augment data as it passes between processes.
Let's create a couple of filters to see how they work.
We'll start by defining a very simple filter. Filters are defined in the same way as functions, but instead of using arguments or $input, we again make use of the special variable $_:
    MSH D:\MshScripts> filter double { $_ * 2 }
Let's take the new filter for a test drive. To begin, we'll use some simple values to seed the pipeline:
    MSH D:\MshScripts> 
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
What's Next?
Now that we've spent time discussing the basics, it's time to explore some of the other features offered by the shell—in particular, the environment MSH provides for running scripts. In the next chapter, we'll see more about variables and scoping, as well as how MSH treats wildcards in text matching, property selection, and filename completion throughout the shell.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Chapter 4: Managing MSH Scope and State
So far, we've seen a number of different aspects of MSH, including the idea of cmdlets, pipelines, and the shell language of variables, functions, and filters. In this chapter, we'll look at the MSH infrastructure that brings all of these components together and allows them to work seamlessly with each other.
In the following pages, we'll look at some of the services that MSH provides. The material we'll cover here is applicable throughout the shell, and tools such as text matching and regular expressions will likely become an indispensable part of your toolkit.
We've already touched on the idea that variables defined in functions might not always be accessible from other functions, scripts, or cmdlets. This limited visibility is known as scoping , and it is used by MSH to segregate data when several different script blocks and cmdlets are in play. It's important to realize that scoping doesn't offer privacy or security; instead, the ability to hide is used to simplify the authoring of scripts. As we'll see, this behavior comes in handy with more complicated scripts and tasks as it reduces the potential for these script fragments to interfere with each other.
In general, MSH controls scope automatically; scripts and functions often "just work" as a result. However, it's always wise to understand how scoping comes into play, especially in cases in which there's a need to do something differently.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Control Access to Variables and Functions
We've already touched on the idea that variables defined in functions might not always be accessible from other functions, scripts, or cmdlets. This limited visibility is known as scoping , and it is used by MSH to segregate data when several different script blocks and cmdlets are in play. It's important to realize that scoping doesn't offer privacy or security; instead, the ability to hide is used to simplify the authoring of scripts. As we'll see, this behavior comes in handy with more complicated scripts and tasks as it reduces the potential for these script fragments to interfere with each other.
In general, MSH controls scope automatically; scripts and functions often "just work" as a result. However, it's always wise to understand how scoping comes into play, especially in cases in which there's a need to do something differently.
In interactive mode, we are working in the global scope, in which any variables or functions defined are accessible from everywhere within the shell:
    MSH D:\MshScripts> function showA { write-host $a }
    MSH D:\MshScripts> $a = 10
    MSH D:\MshScripts> 
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!