Section 7.2. Parse Text-Based Application Output


7.2. Parse Text-Based Application Output

Many administrators find a handful of command-line tools invaluable in their day-to-day tasks. Using the & syntax, we've seen how simple it is to work these tools into newer MSH scripts, but that's only half of the challenge. What is to be done when these legacy tools generate output? cmd.exe batch files and most command-line utilities are still limited to generating textual output, but given the string manipulation functionality in MSH, that needn't be an obstacle.

7.2.1. How Do I Do That?

We'll focus on a commonly used tool for diagnosing network status and problems: ping. The ping tool ships with Windows and is used to send a series of ping packets to a destination to see whether the destination machine is reachable over the network. Let's see some typical output of the ping tool:

     MSH D:\MshScripts> ping 127.0.0.1     Pinging 127.0.0.1 with 32 bytes of data:     Reply from 127.0.0.1: bytes=32 time<1ms TTL=128     Reply from 127.0.0.1: bytes=32 time<1ms TTL=128     Reply from 127.0.0.1: bytes=32 time<1ms TTL=128     Reply from 127.0.0.1: bytes=32 time<1ms TTL=128     Ping statistics for 127.0.0.1:         Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),     Approximate round trip times in milli-seconds:         Minimum = 0ms, Maximum = 0ms, Average = 0ms

That output looks regulara characteristic that makes it a good candidate for parsing. We'll start with a function called IsMachineUp, which will invoke ping and determine whether valid replies were received. This function calls ping.exe, collects each line of its output, filters to show only those rows containing the Reply text, and uses that to conclude whether the ping succeeded:

     MSH D:\MshScripts> function IsMachineUp {     >>$lines = $(&"ping.exe" $args[0] | where {$_ -match "^Reply.*bytes=" })     >>($lines.Count -gt 0)     >>}     >>     MSH> IsMachineUp www.oreilly.com     True     MSH> IsMachineUp nonexistentmachine     False

We can take this one step further and actually convert the results into a more comfortable format for use inside MSH. The following function builds on the same idea as before, but this time uses some string manipulation to extract the interesting fields from the Reply lines:

     MSH D:\MshScripts> function ping {     >>$lines = $(&"ping.exe" $args[0] | where {$_ -match "^Reply"})     >>foreach ($line in $lines)     >>{     >>  $parts=$line.Split(" ")     >>  $bytesPair=$parts[3].Split("=")     >>  $bytes=$bytesPair[1]     >>  $timePair=$parts[4].Split("=<")     # separated by < or =     >>  $time=$timePair[1].Replace("ms","")  # drop ms suffix     >>  $ttlPair=$parts[5].Split("=")     >>  $ttl=$ttlPair[1]     >>    1|select-object '     >>    @{expression = {$bytes}; name = "Bytes"},'     >>    @{expression = {$time}; name = "Time"},'     >>    @{expression = {$ttl}; name = "TTL"}     >>}     >>}     >>     MSH D:\MshScripts> ping www.oreilly.com     Bytes                      Time                       TTL     -----                      ----                       ---     32                         167                        244     32                         101                        244     32                         97                         244     32                         106                        244     MSH D:\MshScripts> ping www.oreilly.com | measure-object -Property Time     -Average     Count    : 4     Average  : 112.75     Sum      :     Max      :     Min      :     Property : Time

7.2.2. What Just Happened?

Before we dig into how this example works, let's take a step back and look at some of the consequences of this approach. Although we've successfully transformed the output of the ping command into a structured format that can be used elsewhere in the shell, we've undermined a few of the key principles that give MSH its flexibility. By choosing to parse the text-based output of a command, we've reduced the robustness of the solution. If the output of the ping command were to change its format in response to a network condition, the parsing rules we've assumed might become invalid.

With that caveat in mind, let's look at what just happened. In the first function, the goal is to capture the output of the ping command with a variable. Due to the presence of the where cmdlet, we can be confident that any lines matching the regular expression, which looks for successful ping responses, will be captured. To give a yes/no state of the availability, all that remains is to see whether any successful ping responses were found. Even just one reply line in the $lines array indicates that the remote machine returned something, so the function simply checks to see that one or more lines are present.

Taking it a step further, we can actually break up each line of the output into its constituent parts. Using the Split method of the string class inside a loop, each of the three data pointsbytes returned, time taken, and TTLcan be separated into their own variables. select-object is then used to place a new object, with its fields populated, on the pipeline for each response. These objects become first-class citizens of MSH, and any of the familiar cmdlets can now be used on the data they hold.

7.2.3. What About ...

... Extending the text-parsing approach to other command-line tools? Although anything that outputs text-based information can be parsed by an MSH script in this fashion, anything that relies heavily on string parsing will be brittle and prone to failure if the format changes in the future. Although ipconfig could be parsed to get a machine name, IP address, and MACor the net use output interpreted to understand mapped drivesall of this system configuration and state information is available via other channels, such as WMI and the .NET Framework Class Library.




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