Map
The final operator is Perl's map function. Map's syntax is similar to grep's syntax. However, the purpose and behavior of the two functions are different. You use grep to find matching elements in a list; you use map to transform a list. That is, map creates a new list based on the contents of another list.
In the example
@users = map(/(\S+)\s+/g, @newresults);
I use a regular expression (/(\S+)\s+/g) as the first argument and the @newresults array from Table 1 as the second argument. Like grep, map successively applies the expression or code block to each element in the list. The regular expression looks for one or more non-whitespace characters, the (\S+) part, followed by one or more whitespace characters, the \s+ part. The g, or global modifier, tells the script to find all occurrences of this pattern in the array element. The parentheses around the \S+ group the pattern substring I want returned. For the contents of the @newresults list in Table 1, the @users array contains the values shown in Table 2.
The Script
Let's apply this basic understanding of backticks, grep, and map in a script. Listing 1, lstlgn.pl, uses NT's built-in NET USER command to obtain information about the last logon date and time for all users on the server or domain, and generates a report listing that information.
The script first invokes NET USER to create the list of users on the system. Then the script loops through this list a second time invoking NET USER username to obtain the desired data for each user. The result will be a neatly formatted report of all users and their associated last logon date and time stamp.
The code at callout A creates the user list. At A, you can see where the @users array is set using the results of the map command. The map command in turn is using the output of the grep command as input. Likewise, the grep command is using the output of the NET USER command as input. To help you understand how this works, let's look at the different stages of this process.
The NET USER command at the end of callout A produces the output in Stage 1 of Figure 2. Grep treats each line of NET USER's output as one list element. I use an if/else code block as grep's first argument. The code block tells grep to return a section of the output rather than elements based on a pattern.
The code block tells grep to return any elements located between a line that contains one or more hyphens and a line containing the string The command. For this task, I control the return value of grep's code block with the control variable $i. Grep returns an element only when the expression or code block is true.
As grep evaluates each line of NET USER's output, the if part of the code block tests whether the line contains one or more hyphens followed by a whitespace character ( /-+\s/ ). If it does, the code sets the control variable to 1 (true) but forces the if part of the block to return false with the 0; statement. You don't want the line with hyphens returned.
When grep evaluates the next element, the else part of the block will execute. With $i set to true, grep will return the lines containing usernames until it reaches a line that matches the /The command/ pattern. At this point, the code block sets the control variable back to 0 (false). Stage 2 of Figure 2 shows the elements grep returns.
Grep's return values then pass to the map command, which initializes the @users array as Stage 3 of Figure 2 shows. The code at B, page 208, opens the output report named lstlgn.txt and writes the title and column headings to it.
The foreach loop comes next. Through each iteration, the script assigns an element of the @users array to the scalar variable $user. The script then executes NET USER a second time, passing to `net user $user` the current username. This procedure produces the output in Stage 4 of Figure 2, which becomes the source list to the map function. This time I use map to transform the list of elements that NET USER username provided into a one-element list containing only the data I'm interested in: the last logon date and time stamp.
The regular expression /Last logon\s+(\S+\s\S+\s\S+|Never)\s+/ is the pattern that tells map what you're looking for. The regular expression tells the script to match the line that begins with the string Last logon, followed by one or more whitespace characters \s+, followed by three groups of non-whitespace characters separated with single spaces \S+\s\S+\s\S+ or ( | ) the string Never, and ending with one or more whitespace characters \s+.
I use the parentheses around the pattern substring I want returned. The result is the single-element @lastlogon list in Stage 5 of Figure 2.
The script then writes this data to the report file, undefines the @lastlogon array, and moves on to the next user in the @users list. The write function makes use of the REPORT format defined at C in Listing 1, page 208, which produces the neatly arranged lstlgn.txt output file in Stage 6 of Figure 2. Formats are a Perl feature that give you precise control over the format of reports. The REPORT format defines two left-justified (by way of the less than symbol), fixed-width fields to which the contents of the $user and $lastlogon[0] variables are written. Formats behave in a way similar to functions. When the script encounters the write(REPORT); statement, execution jumps to the REPORT format definition which dictates the output of write. Format definitions must end with a period on a line by itself. The script ends by closing the output file with a call to the close function, close(REPORT).
At Your Command
With the strength of Perl's text-processing facilities, you can easily extend the power of NT's command-line utilities. Sometimes the tool you're looking for is staring right at you; all you need is a bit of creativity.
End of Article
Prev. page
1
[2]
next page -->