Section 3.4. Control Script Flow with Comparisons


3.4. 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.

Echoes of GOTO

Although flow control is possible in cmd.exe, its reliance on labels, simple IF tests, and GOTO commands can make for some complicated batch files.

With the if and switch constructs in MSH, controlling script flow becomes more intuitive and a lot easier to read after the fact. MSH doesn't carry over the concept of GOTO from cmd.exe because it simply isn't needed. Cases where GOTO is used in a batch file to send execution off to a section of reusable commands that can be replaced with functions is a concept we'll look at later in this chapter.


3.4.1. How Do I Do That?

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 else and elseif:

     MSH C:\WINDOWS\system32> if ((get-childitem).Count -gt 50) { "More than 50 items in this folder" }     More than 50 files in this folder

Let's build this up into a more complete example. The script in Example 3-1 checks the number of files in a folder against a known expected value and then reports its findings. In this example, we'll only run this check on the first day of the month (which would be useful if this formed part of a larger script that was run daily).

Example 3-1. DayOneReportCheck.msh
 $statusReportCount = (get-childitem "D:\StatusReports").Count $departmentCount = 23 $dayOfMonth = (get-date).Day if ($dayOfMonth -eq 1) {     if ($statusReportCount -eq $departmentCount)     {         "All departments accounted for"     }     elseif ($statusReportCount -lt $departmentCount)     {         "Missing"+($departmentCount-$statusReportCount)+"reports"     }     else     {         "Too many reports found!"     } }

In Example 3-1, we've implicitly combined two tests: making sure it's the first day of the month and checking the number of reports. It's often convenient to collapse several tests into a single condition using the -and and -or operators. Example 3-2 has a different script that might be used as a reminder at the start of each month.

Example 3-2. DayOneCleanupReminder.msh
 $dayOfMonth = (get-date).Day $workingFiles = (get-childitem "$MyDocuments\Ongoing").Count if (($dayOfMonth -eq 1) -and ($workingFiles -gt 50)) {     "A new month is here; please clean out $MyDocuments\Ongoing." }

The if statement on its own gives complete power to control what happens next, but there are situations in which there are several different ways to proceed, depending on a variable's value. Implementation of these more complex decisions with if statements can quickly lead to a tangled collection of elseif statements, which can be hard to read and maintain in the long run. In these cases, it can be more convenient to use a similar but different construct: the switch statement. The switch statement combines several tests into single blocks, and it generally makes such scripts easier to read, as shown in Example 3-3.

Example 3-3. DayOneReportCheckSwitch.msh
 $statusReportCount = (get-childitem "D:\StatusReports").Count $departmentCount = 23 $dayOfMonth = (get-date).Day if ($dayOfMonth -eq 1) {     switch ($statusReportCount)     {         {$_ -lt 5}         { "Very few reports present (<5)" }         {$_ -gt 4 -and $_ -lt 10 }         { "Many reports missing (5-10)" }         {$_ -gt 9 -and $_ -lt 20 }         { "Usual report count (10-20)" }         {$_ -gt 19 -and $_ -lt 23 }         { "Most reports present (>20)" }         23         { "Perfect" }         default         { "Unexpected number of reports present" }     } }

3.4.2. What Just Happened?

The comparison operators -gt and -lt appear consistently throughout the examples in this section and they, along with the other comparison operators, are the vital ingredient when controlling script flow. All comparisons result in a Boolean result where the outcome will either be true or false.

Comparison operators will work consistently on a wide range of data types. In all cases, the equality test -eq will be available, whether comparing numbers, strings, collections, or objects. For simple variable types such as numbers and strings, additional operators such as -gt and -lt become available (performing numeric and alphabetic comparisons, respectively). Other operators, such as -and and -or, are used for comparing Boolean types. We saw a few of the comparison operators in Chapter 1; the full list can be found in Appendix A.

Once we were able to define a conditional test to determine something about the environment, we looked at two language constructs used to control script flow. Let's take a look at these in more detail.

3.4.2.1. The if statement

The if statement takes the form of a condition followed by a script block. Not all parts of the following syntax are required and some may appear more than once:

     if (<condition 1>)     { <block 1> }     elseif (<condition 2>)     { <block 2> }     else     { <block 3>}

If <condition 1> is satisfied, the content inside of <block 1> will be run. Conditions are tested as Boolean values, and all comparison operators return results of that type. If <condition 1> wasn't met (it evaluated to false), flow skips over <block 1> and on to any elseif statements that might be present. Although elseif statements are not required, there may be any number of them present. Each elseif condition is evaluated in turn, and as soon as a match is found, the corresponding script block is run. If not one of the conditions is met, flow proceeds to the optional else block; the final <block 3> is run if it is present.

If any of the conditions are true, the corresponding block is executed. When that block is complete, the whole statement is finished and no further tests are performed. Even if several elseif conditions are true, only the block for the first will ever be run.

3.4.2.2. The switch statement

The switch statement, although similar to if, has a different syntax and a wider range of uses:

     switch (<test variable>)     {         <literal> { <block 1> }         { <test block> } { <block 2> }         default { <block 3> }     }

Every switch statement focuses on the idea of comparing the <test variable> against something else. The test could be a shell variable, a property, an object, or even a set of objects coming out of a pipeline.

Within the body of the switch statement, there are three types of comparisons that can occur:


Literal comparison

If a literal (such as a number or string) is present, MSH effectively checks <test variable> -eq <literal> and will run <block 1> if the test evaluates to true.


Test block

An expression is included in braces for evaluation. Inside the expression, $_ is used to represent the current <test variable> value. If the expression evaluates to true, the corresponding <block 2> is run.


Default

The default keyword may only appear once in a switch statement and is equivalent to the else part of the if statement. <block 3> is executed if and only if none of the other matches are made.

Flow through a switch statement is notably different than the flow through an if statement. With a switch statement, where multiple matches are made, each of the corresponding script blocks is run in sequence. As a rule of thumb, if you can do it with an if and an elseif, you should. But if you find yourself needing many elseif cases or repetition in the script blocks, it's probably a good opportunity to rework the logic with a switch statement.

3.4.3. What About...

...Comparing incompatible types? What happens if you try to see whether a number is "greater" than a word? When MSH sees incompatible types in a comparison, it attempts to convert the second to the same type as the first, and then it compares the results. Order is sometimes significant here, so be careful, as the outcome may not always be what you expect:

     MSH D:\MshScripts> (10 -gt "Hello")     The '-gt' operator failed: Cannot convert "hello" to "System.Int32".     Error: "Input string was not in a correct format."..     At line:1 char:8     + (10 -gt  <<<< "hello")     MSH D:\MshScripts> ("Hello" -gt 10)     True

In the first case, MSH tries to convert the word "Hello" to a number and fails, stating the cause. However, note that in the second case, the number 10 is converted to a string without complaint. (This automatic conversion of numbers to strings is usually very convenient.) When strings are compared, the results depend on the alphabetical ordering of the characters within them. Because the digits are considered to occur before the alphabet, the digit 1 is seen as less than A, and therefore less than H; MSH does the best it can and returns a result based on this ordering.

3.4.4. Where Can I Learn More?

Get-help offers the full list of comparison operators with the keyword about_Comparison_Operators.




Monad Jumpstart
Monad Jumpstart
ISBN: N/A
EAN: N/A
Year: 2005
Pages: 117

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net