Sometimes scripts can be short and sweet
Q: I'm trying to send a small .wav file to a remote Windows 2000 workstation using alternate credentials . I searched the Web and found some code (see Listing 1) that I though would help. However, the code fails on the last line, and I receive an Access Denied message. The problem doesn't lie with the credentials. When I type the IP address in a Microsoft Internet Explorer (IE) session and enter the username and password, I successfully gain access to the machine. I posted this problem on ScriptingAnswers.com (http://www.scriptinganswers.com). The person who responded told me that the FileSystemObject object and Windows Management Instrumentation (WMI) are two different things and that there isn't any support for alternate credentials when you're trying to copy files with the FileSystemObject object. The person said that I didn't need to use WMI and suggested using a batch file instead. He said the batch file should specify the alternate credentials with the Net Use command, map a drive to the remote computer, copy the .wav file, then remove the mapped drive. I created such a batch file, but it runs slowly and produces inconsistent results. Can you help?
A: ScriptingAnswers.com started steering you in the right direction. Rather than using WMI to specify the alternate credentials and connect to the remote computer, you can use the Net Use command. However, you don't need to map to a drive. Instead, you can use the approach demonstrated by CopyFile.cmd in Listing 2.
To make it easy for an administrator to use CopyFile.cmd to copy any file to any remote computer, all the necessary information is provided on the command line rather than hard-coded in the script. To run the script, you follow the syntax
copyfile.cmd file_to_copy destination username password
where file_to_copy is the pathname of the file you want to copy and destination is the full path to the location in which you want to copy that file. You need to enclose the paths in quotes if they contain spaces. The username and password arguments specify the alternate credentials that you want to use to connect to the remote machine. The username should follow the format DomainName\Username and must be an account with permissions to write to the destination path.
For example, suppose you want to use the username and password of alphadomain\admin and a1b2c3z26, respectively, to copy the newchime.wav file in your C:\WINDOWS\Media\Microsoft Office folder to the admin$\Media\Microsoft Office folder on a remote workstation named wkstn248. In this case, the launch command would be
"c:\windows\media\microsoft office\newchime.wav" "\\wkstn248\admin$\media\microsoft office" alphadomain\admin a1b2c3z26
(Although this command appears on several lines here, you would enter it on one line in the command-shell window.)
When CopyFile.cmd runs, it checks to see whether you entered all four command-line arguments by making sure a fourth argument exists. As callout A in Listing 2 shows, it does so by using an If command with the string1
string2 clause. This type of If command tests whether two strings are identical, and if so, executes the code that follows. The syntax for this usage is string2 CodeToExecute
where string1 and string2 are either literal strings or batch variables representing strings (e.g., %1), and CodeToExecute is the code you want to run if the strings are identical. (Note that the strings are case sensitive unless you include the /i switch. For more information about the For command, see the For Web page at http://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/for.mspx?mfr=true. When string1 or string2 is batch variable, you must enclose it in quotes. (Although not mandatory, it's a good idea to also enclose literal strings in quotes because the If command will fail if an unquoted literal string contains spaces.) So, in our case, the command
compares the string represented by the %4 variable (i.e., the password entered on the command line) to an empty string. If the fourth argument is an empty string (which means four arguments weren't provided), the script goes to the section of code marked by the :syntax label.
In callout A, note the use of the number signs (#) instead of quotes. Enclosing batch variables in quotes can lead to problems. For example, suppose an administrator is big fan of the TV show "The Simpsons," so his password is "Doh!" If CopyFile.cmd had used the command
If "%4""" Goto :syntax
the command would evaluate to
If ""Doh!""=="" Goto :syntax
during runtime. The two consecutive quotes before and after Doh! would trigger a syntax error, which would cause the script to fail. Granted, passwords that start or end with quotes probably aren't too common, but what if string1 or string2 is a batch variable that evaluates to a path, such as the %1 and %2 variables in CopyFile.cmd? Paths are often enclosed in quotes, so syntax errors are likely.
To avoid this problem, you can use number signs rather than quotes, as callout A shows. The use of the number sign is arbitrary. You just need to use a character other than quotes—but the number sign hasn't failed us yet. It's unlikely that people will use a number sign at the beginning or end of paths, passwords, usernames, or other parameters entered on the command line.
Assuming that you entered all four arguments on the command line when launching CopyFile.cmd, the script assigns those arguments to variables, then uses the Net Use command to connect to the remote computer. Net Use is a versatile command that has many optional parameters and switches, which you can learn about in the Net Use Web page (http://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/net_use.mspx?mfr=true). In this case, we're following the syntax
Net Use \[\\ComputerName\ShareName\] \[password\] \[/u:\[DomainName\\]Username\]
where \\ComputerName\ShareName specifies the target remote computer, and password and DomainName\Username specify the alternate credentials to use to connect to that machine. As the line of code in callout B shows, the %destination%, %password%, and %username% variables provide this information. The code > NUL at the end of the line redirects the Net Use command's output to NUL (aka the command-shell trash can) so it isn’t visible onscreen. (For more information about redirecting output to NUL, see "Shell Scripting 101, Lesson 4," http://www.windowsitpro.com/Article/ArticleID/20530/20530.html.)
After the Net Use command, the script uses an If command with the Errorlevel clause to determine whether the Net Use command executed successfully. Most shell-scripting commands return an errorlevel value when they execute. The errorlevel value of 0 indicates success; the errorlevel value of 1 or higher indicates failure. So, the If command checks to see whether the Net Use command’s errorlevel value is not equal (NEQ) to 0. If that condition is true, a failure message displays and the script jumps to the end of the file, where it terminates. Notice that although the Goto command points to the :EOF label, the script doesn't include a :EOF label. You don't need to explicitly include :EOF because it's a built-in label.
When a successful connection to the remote machine occurs, CopyFile.cmd uses the Copy command to copy the file, as callout C shows. Like the Net Use command, the Copy command has numerous optional parameters and switches. In this case, we're following the basic syntax
Copy \[/y\] source destination
where source specifies the file you want to copy and destination specifies the location in which to copy the file. The /y switch tells the command processor to automatically overwrite an existing file if one is present in the location you're copying the file to. In other words, you won't receive a prompt asking you to confirm that you want to overwrite an existing file.
Finally, the script checks the errorlevel value returned by the Copy command. When the errorlevel value is not equal to 0, the script displays a failure message. Otherwise, the script displays a message that notes the file was successfully copied.
Because CopyFile.cmd is short and simple, it should run quickly. In tests, its results were consistent. As CopyFile.cmd shows, sometimes scripting a task is easier than you might have initially thought.