Getting Started in NT Shell Scripting, Part 2
This article is the last part of a two-part series on how to begin writing scripts in the Windows NT shell scripting language. This installment shows you how to transform pseudocode into a script and how to test that script.
March 19, 2000
Last month, I showed you how to go from having an idea for a script to determining that script's requirements and writing the pseudocode. This month, I show you how to transform the pseudocode into a script and how to test this script.
You'll be creating the script ShareCapture.bat, which periodically captures share settings. Its output creates another script, RecreateShare.bat, which rebuilds shares if they disappear. Listing 1, page 2, contains a step-by-step explanation of what is occurring in ShareCapture.bat. You can download ShareCapture.bat from the Code Library on the Win32 Scripting Journal Web site at http://www.win32scripting.com. The downloadable version doesn't include the step-by-step explanation because this information would interfere with the script's execution. The Code Library also includes an example of RecreateShare.bat.
Transforming Pseudocode Tasks 1 and 2
After you write the pseudocode, you need to transform it into Windows NT shell scripting code. First, you need to write the code that captures the share information (i.e., pseudocode task 1). To accomplish pseudocode task 1, you use the basic Rmtshare command
Rmtshare \work1
Next, you need to filter that command's output to remove the lines you don't want (i.e., pseudocode task 2). You must eliminate several lines, so let's begin by removing the lines that contain the administrative shares and the line that says the command completed successfully. To remove these lines, you use the Findstr command with two switches:
| Findstr /l /v "IPC$ ADMIN$ successfully."
In this command, the pipe (|) symbol passes the output of the preceding command (i.e., Rmtshare) to the Findstr command. Findstr then looks for strings in the output that match the strings inside the quotes. You can tell Findstr to search for more than one string; in this case, you're telling Findstr to search for three strings: "IPC$," "ADMIN$," and "successfully." The /l switch tells Findstr to look for these strings as literal text. If you remove the /l switch, some values you want to filter out will remain because the dollar sign is a special character that specifies the end of the line to Findstr. IPC$ and ADMIN$ aren't the last word on the line, so they're not a match unless you use the /l switch to tell Findstr to ignore the special character status and search for a literal match. The /v switch makes Findstr inverse the output—that is, Findstr locates and outputs the lines that don't match the search criteria. Without this switch, Findstr locates and outputs the lines that match the search criteria.
Now that you've filtered out the administrative-share and miscellaneous lines, you need to filter out the default-share lines. You again pipe Rmtshare's output to Findstr with the command
| Findstr /b /i /v /c:"[A-Z][$]"
This time, you use several new switches (/b, /i, /c:) because you don't always know what drive letters exist on a given server. The /b switch tells Findstr that the strings it's searching for must be at the beginning of the line. The /i switch makes the search case insensitive. The /c: switch tells Findstr which strings to search for. In this case, Findstr is to search for any member of the set, or class, of characters A through Z ([A-Z]) that is followed by a dollar sign ([$]). In other words, Findstr is to search for A$, B$, C$, and so on.
The /c: switch allows spaces in the search string. Although this feature doesn't apply to this example, you'll likely find it useful for other scripts you write. For example, if you want to find the string "Fred likes Susan", you need to use the /c: switch. Otherwise, Findstr will locate all the lines that contain "Fred", "likes", or "Susan".
When you put all the Rmtshare and Findstr components together, you obtain the filtered Rmtshare command
Rmtshare \work1 | Findstr /l /v"IPC$ ADMIN$ successfully."| Findstr /b /i /v /c:"[A-Z][$]"'
Figure 1 contains this command's output. (I've numbered the lines in the figures for clarity. These numbers don't appear in the actual results.) As you can see, only lines 5 through 11 contain the information you need. Thus, you still need to filter out the remaining lines. You can use the For command to not only filter out these lines but also capture the share names from lines 5 through 11 and place them into environment variables, or simply variables.
A variable is a scripting feature that lets you store data. You can think of a variable as a data storage container that can hold different data at different times. You use this container to transport data from one part of a script to another.
You use the Set command to assign a value to a variable. For example, to set the value Work1 to the variable named Servername (variable names are case insensitive), you type
Set Servername=work1
To retrieve and use a variable's value, you need to enclose the variable in percent signs (%). For example, to retrieve and display the Work1 value from the Servername variable, you type
Echo %Servername%
The For command has several syntaxes that let you perform different operations. In this case, you want to use the syntax
For /f [options] %%var In ('CommandA')Do CommandB
which lets you break apart, or parse, the output of CommandA into pieces, capture only the pieces that meet the criteria you specify in the options, and then perform CommandB for each captured piece.
Step 3 in Listing 1 (which begins on the third line) uses this syntax. To better understand how this For command works, let's dissect each segment. The first segment
For /f "tokens=1 skip=4" %%i In
provides the parsing details. The /f switch specifies that you want to parse the output of the filtered Rmtshare command into pieces, or tokens. By default, the For command uses a space or tab as the delimiter (i.e., the character that marks the beginning or end of a token).
As Figure 1 shows, the lines of output contain a range of token counts. For example, line 5 contains four tokens, whereas line 9 contains seven tokens. (Remember that I added the numbers at the beginning of the lines for clarity. The numbers don't appear in the actual output, so you don't include them in the token count.) In this case, because you're interested only in the share name (i.e., token 1), it doesn't matter if the number of tokens per line isn't consistent.
The code "tokens=1 skip=4" specifies that you want to discard, or skip, the first four lines of output and, starting with line 5, keep token 1. The lack of a delims= option specifies that you want to keep the default delimiter.
The code %%i captures token 1 in an iterator variable. This special type of variable serves as a temporary container for data that you've captured in a For command. Iterator variables exist only within the particular For command you're using them in. Typically, the first iterator variable is named %%i; subsequent iterator variables proceed through the alphabet from that point (e.g., %%j, %%k). Unlike environment variables, iterator variable names are case sensitive. When you use an iterator variable in a script, you use a double percent sign (e.g., %%i). When you use an iterator variable in a command prompt, you use one percent sign (e.g., %i).
The second segment
('Rmtshare \%Servername%^| Findstr /l /v "IPC$ ADMIN$successfully." ^| Findstr /b /i/v /c:"[A-Z][$]"')
provides the output that the For command parses. This code is the filtered Rmtshare command with three minor adjustments:
Previously, you specified, or hard-coded, the server name Work1 in the filtered Rmtshare command. As a result, if you were to use the script for any other server, you would need to change the server name in this command. Constantly changing hard-coded names in scripts can get tedious. Fortunately, you can avoid this situation by using variables instead of hard-coded names in commands. Thus, instead of specifying Rmtshare\work1, you put Rmtshare \%Servername%. To let the scripting engine know which server you want, you use the Set command to assign the server's name to the Servername variable.
The NT shell scripting language has several reserved shell characters (i.e., characters that have a special meaning in the NT system). If you use these characters outside a string (i.e., outside double quotes), you need to flag, or escape, the reserved character by preceding it with the carat symbol (^). Because the pipe symbols in the filtered Rmtshare command are reserved shell characters, you need to precede each one with a carat symbol.
To tell the For command where the command that provides the output to parse (i.e., the output command) starts and stops, you need to enclose the output command in parentheses and single quote marks. Thus, you need to add the parentheses and single quote marks to the filtered Rmtshare command.
The final segment
Do (Set Share=%%i) &(Call :SECONDTRY)
tells the For command to carry out two commands for each token it captures. The ampersand (&) between the two commands tells the For command to perform the commands in the order given. First, the For command assigns the share name in %%i to the variable called Share. Then, the For command sends the scripting engine to another part of the script labeled :SECONDTRY.
Within an NT shell script, the scripting engine typically starts at the top of a script and systematically works its way down each line of code. However, you can change this flow with the Call and Goto commands. The Call command tells the scripting engine to go from the current location (point A) to another part of the script (point B), perform the tasks that the code at point B specifies, and then return to point A. In contrast, the Goto command tells the scripting engine to go from point A to point B and then perform point B's tasks—the scripting engine doesn't return to point A when it's finished with those tasks.
To specify where point B is, you use a label, which is a string prefaced by a colon (e.g., :SECONDTRY). You place this label in two spots: after the Call or Goto command and before the code at point B.
Transforming Pseudocode Tasks 3 and 4
Now that you've run the initial Rmtshare command and filtered its output to capture the share names, you need to obtain the share's remarks and absolute paths (pseudocode task 3) and output all the share information to RecreateShare.bat (pseudocode task 4). The :SECONDTRY procedure in steps 7 through 10 in Listing 1 performs these remaining tasks.
As step 8 shows, the :SECONDTRY procedure begins with a For command that iterates through the output of a second Rmtshare command that captures then filters the details of a specific share. In this Rmtshare command, the segment
Rmtshare \%Servername%%Share%
is the same command that you used in the planning stage to capture the WorkingDir share's absolute path, except this time you're using %Servername% and %Share% instead of the server name and share name.
Rmtshare returns the specified share's details, which will look like the output in Figure 2. You use the Findstr command to search this output for the line that contains the literal string "Remark" and the For command to parse that line. The code "tokens=1,*" %%i tells the For command to assign token 1 to %%i and to assign all the remaining tokens in that line (which is what the asterisk specifies) to %%j. (Rmtshare can capture up to 34 characters, including spaces.) As a result, %%i contains the word Remark and %%j contains the description (e.g., working dir share). Because you want to capture only the description, you use the Set command to assign %%j to %Remark% but don't do anything with %%i.
In some cases, the share details might include the word Remark but not a description. Thus, you need to test whether %Remark% is empty with the If command, which conditionally executes code. Like the For command, the If command has several syntax versions. You need to use the version that tests for equality. This version has the syntax
If str1
str2 CommandA
where str1 and str2 are the strings you're testing for equality, which the double equal signs () specifies. CommandA is the code that the If command executes if str1 and str2 are equal. (This NT shell scripting command is equivalent to VBScript's If...Then...Else statement.)
In step 9 in Listing 1, the If command
If "%Remark%"==""
tests to see whether the string that %Remark% contains is equal to an empty string, which the empty set of double quotes ("") specifies. If the strings are equal (i.e., %Remark% is empty), the For command in step 9 executes. If the strings aren't equal (i.e., %Remark% isn't empty), the For command in step 10 executes. Here's what happens in each case.
%Remark% is empty. Similar to the For command in step 8, the For command in step 9 executes Rmtshare to obtain and then filter the share details. However, this time the For command keeps the line containing the string "Path" and assigns token 2 in that line to %%i. As Figure 2 shows, token 2 contains the share's absolute path (c:CollaborationTeamAWorkingDir).
The For command then executes the Echo and Goto commands. You use Echo to generate, or echo, output to either a console window or to a file you specify with the redirection symbol (>>). In this case, you're echoing the text Rmtshare \, the value in %Servername% followed by a backslash, the value in %Share% followed by an equals sign, the value in %%i, and the text /unlimited switch to RecreateShare.bat. So, for example, the completed line of code in RecreateShare.bat for the C-Drive share is
Rmtshare \work1C-Drive=C: /unlimited
The inclusion of Rmtshare's /unlimited switch means the rebuilt C-Drive share will have an unlimited number of users.
After echoing the output to RecreateShare.bat, the For command executes the Goto command. This command sends the flow to the procedure labeled :LAST.
%Remark% isn't empty. The For command in step 10 is the same as the one in step 9, except for the addition of the code /remark:"%Remark: =%". This code causes Echo to include the text /remark: followed by the %Remark% variable's value enclosed in double quotes. (Rmtshare requires that you surround any remarks containing spaces with double quotes.) Thus, for example, the completed line of code in RecreateShare.bat for the Sales share is
Rmtshare \work1Sales=C:Sales/unlimited /remark:"Sales andMarketing Team Folder"
The addition of the colon, two spaces, and equals sign in the "%Remark: =%" code tells Echo to remove any pairs of extra spaces between the text /remark: and the quoted value. If you have more than one extra space, RecreateShare.bat might encounter an error when trying to execute that line of code.
After the For command in step 10 executes, the script proceeds to the :LAST procedure. This procedure uses the code Goto :EOF to take the flow back to where you called the :SECONDTRY procedure in step 3. (:EOF is a preset NT shell label for End Of File.) The For command in step 8 then selects the next share and the process repeats until there are no more shares left to loop through.
Running the Scripts
ShareCapture.bat doesn't support share names or paths that include spaces. In addition, because I wanted to keep this script simple, ShareCapture.bat doesn't deal with share permissions. (If you want to capture share permissions, you can add additional loops with the For command.)
To test ShareCapture.bat and RecreateShare.bat, follow these steps:
Create several shares on your test server, if you haven't done so already. Include remarks for some of the shares. If you want to keep track of the share names, paths, and remarks you enter, you can create a spreadsheet containing this information.
In ShareCapture.bat, modify the path and filename of RecreateShare.bat, if necessary.
At the command prompt, run ShareCapture.bat by typing
ShareCapture.bat Servername
where Servername is the name of the server on which you're running the script.
Review the output in RecreateShare.bat to make sure the results match the information you entered in the spreadsheet.
Delete all the shares you created.
At the command prompt, run RecreateShare.bat by typing
RecreateShare.bat
Verify that RecreateShare.bat correctly rebuilt all the shares.
If both scripts execute successfully during the test, you can run them in production mode. You now have an easy way to rebuild any shares that disappear from your file servers.
About the Author
You May Also Like