And you even get a script that enumerates user profiles to boot
If you ever had to clean up old user profiles on shared PCs or locate all the PCs a specific user has logged on to for security reasons, the GetUser Profiles.bat script will be a valuable addition to your script collection. This script uses the NetUsers tool to enumerate user profiles on PCs. Even if you don't need to enumerate user profiles, GetUserProfiles.bat is valuable in that it demonstrates how to error-proof paths and perform advanced string manipulations.
How to Error-Proof Paths
Script errors are often caused by problems with pathnames to input, output, and utility files. Common problems include paths enclosed in quotes, incorrect paths, and paths with embedded spaces. GetUserProfiles.bat demonstrates how you can error-proof your file paths against these problems.
Dealing with paths enclosed in quotes. When people enter input file paths in scripts, they often enclose the path in quotes. Although that's typically the proper way to handle paths, it can cause problems if you've written a script that adds quotes when they're necessary. Having duplicate quotes can cause a script to fail.
The best way to deal with this situation is to assume that a script's user has entered quotes, use a string-replacement operation to remove them, then add them back in. This technique ensures that the script works correctly regardless of whether the user enters a file's path in quotes. Callout A in Listing 1 shows this technique in action. Note that the outside set of quotes effectively adds the quotes back in.
Dealing with incorrect paths. If you run scripts regularly, you've likely seen the dreaded File Not Found message scrolling across your screen because you entered an incorrect path to a file or tool. The best way to eliminate this problem is to run a test to determine whether the file exists at the specified location. For input files and tools, you can test for file's existence and exit the script with an error message if the file isn't found, as callout B shows. For output files, you need to take a slightly different approach because scripts often create output files near the end of their run time. One approach is to simply test for the existence of the folder in which the output file will be created. Another approach is to create the output file early in the script, write a line that contains header information, then test for the file's existence. This solution tells you not only whether the file exists but also whether it's writeable. As callout C shows, GetUserProfiles .bat uses this approach.
Dealing with paths that contain embedded spaces. There are some utilities, including NetUsers, that don't work correctly when you execute them from within in a For command and the utilities' paths include spaces. You can prevent such problems by converting the paths to an 8.3 format. To do so, I first tried to use the %~s1 batch parameter with the For and Call commands but had mixed results. (Failures sometimes occurred when attempting to get the short path while there were still spaces in the filename.) So, I settled on a more reliable approach in GetUserProfiles.bat. As callout D shows, the script first uses the Dir /X command to get the 8.3 filenames, then calls the getshort routine. This routine, which callout H shows, strips off the long filenames from the original pathnames that the user entered, leaving just the long paths to the folders in which the files are stored. The routine then obtains the 8.3 versions of the long paths. Finally, the routine combines the 8.3 filenames and the 8.3 paths to get the entire pathname in a short format. Although writing this code requires a bit of work on the front end, it can eliminate problems later on.
When you perform 8.3 path conversions, you need to make sure that you correctly sequence your scripts. It might be tempting to convert the files' paths to an 8.3 format before checking for the files' existence. However, if you do so, you'll discover that Call, For, and Dir commands don't work correctly. Always test for files' existence before you perform any 8.3 conversions.
How to Perform Advanced String Manipulations
As mentioned previously, GetUserProfiles.bat uses NetUsers. This free utility is available from SystemTools.com, which has a number of interesting freeware tools. You can use NetUsers to query a local or remote computer for a history of the users who have logged onto the machine. The good news is that this tool retrieves their user IDs, friendly names, and last logon date and time. The not-so-good news is that although the NetUsers output is easy to read on screen, it's difficult to work with when you redirect it to a file.
For example, let's say you want to get the history of the users who have logged on to a remote computer named WORK1, so you run the command
NetUsers.exe \\WORK1 /h
The /h switch tells NetUsers to display the users who have logged on in the past. Without this switch, NetUsers displays only the currently logged on user. Figure 1 shows this command's output, which has been redirected to a file. Note that the Connecting to line is returned only when accessing remote computers and is seen only when the output is redirected to a file.
As Figure 1 shows, the NetUsers output contains a lot of extraneous lines. You need only the lines with the user information—in this case, the lines that start with MyDom\ fredsmith and MyDom\donnawong. To filter the NetUsers output so that you get only the lines that contain the user information, you can use the Find command with the /V switch. This switch tells the Find command to display all lines that don't contain the specified text. So, in this example, you use the command
Find /V " -"|
Find /V "successfully."|
Find /V "History"|
Find /V "Connecting"
Figure 2 shows the output from this command. You now have only the lines you need (aka the filtered strings), but there's another problem. The filtered strings contain varying numbers of spaces, which creates problems when you try to embed the strings in a For command. So, you need to do some creative coding to retrieve the four parts—the user ID (i.e., Domain\UserID), the friendly name, and the date and time of last logon—of each filtered string.
Assuming that the user IDs don't contain any spaces, the first part of the filtered string will always be the Domain\UserID. Thus, you can use the For command to break the filtered string into pieces, or tokens, so you can retrieve the user ID. As callout E shows, GetUserProfiles.bat captures the user ID in the first token (%i variable) and assigns the rest of the filtered string to the Remainder variable.
You can't use tokens to retrieve the friendly name because the friendly name might include a middle initial or might consist of several elements separated by spaces (e.g., Le Pu, Pepe), which means the friendly name might be in two or more tokens. For example, if the user has a name with no middle initial (e.g., Smith, Fred), the date would be in the fourth token (%l variable). If the user has a middle initial (e.g., Wong, Donna J.), the date would be in the fifth token (%m variable).
Fortunately, you can perform some advanced string manipulations to get the friendly name and the date and time information. You're probably familiar with using the Set command to perform environment-variable string substitutions. For example, the command
expands the PATH environment variable, then replaces the string on the left side of the equal sign (str1) with the string on the right side of the equal sign (str2).
A less commonly used environment-variable string substitution lets you expand an environment variable and extract a specific number of characters from the end of the expanded string. For example, to expand the PATH environment variable and extract the last 5 characters from the end of the expanded string, you use the command
You can use this type of string substitution to extract the date and time information from the Remainder variable. (You can also use this type of string substitution to extract characters from the beginning of a string. For more information about this substitution, run the Set command with the /? switch.) As Figure 2 shows, the time is the last 5 characters in a filtered string and the date is characters 6 through 16 if you count backward from the end of a filtered string. Thus, you can use the code that callout F highlights to extract the date and time.
With the user ID and date and time information retrieved from the filtered string, all that remains is the retrieval of the friendly name. As callout G shows, you can use a series of string replacements to retrieve the friendly name.
Note that there is always more than one way to handle a task in a script. Another way to retrieve the friendly name would be to use environment-variable string substitution to skip over the last 16 characters in the Remainder variable's string, assign the resulting substring a variable, then remove any double spaces from the substring.
Using the Script
To get GetUserProfiles.bat working in your environment, follow these steps:
- Download GetUserProfiles.bat from the Windows Scripting Solutions Web site. (See page 1 for download information.)
- Download NetUsers.exe from SystemTools.com (http://www.systemtools.com/free.htm). I used version 1.22 for this script.
- Create an input file that contains the machines on which you want to enumerate user profiles. Place one computer name per line. You must have local Administrator authority on the machines you're accessing.
- Configure the paths to the input, output, and NetUsers files. The script's comments detail where to make these configurations.
- Customize the output file if necessary. I set up the output file to be a .tsv file rather than a .csv file because the friendly names in my environment contain commas. If the friendly names in your environment don't contain commas and you prefer the output file to be a .csv file, you can replace the tabs with commas. The script's comments detail where these changes need to be made.
- Initially run the script against one or two test computers. Check the output file to make sure the run was successful before running the script in your production environment.
I ran GetUserProfiles.bat on computers running Windows XP Service Pack 2 (SP2) and Windows 2000 Professional SP4. The test run returned information when run against Windows Server 2003, XP, and Win2K machines.
A Multipurpose Tool
By using GetUserProfiles.bat, you can clean up old user profiles on your shared PC. By looking at how GetUserProfiles.bat operates, you can learn how to error-proof paths in your scripts and perform some advanced string manipulations. No matter whether you use this script for one or both purposes, it can be a valuable tool.