Hey, Scripting Guy! How Can I Copy a File to a Removable Drive If I Don’t Know the Drive Letter of That Drive?
Hey, Scripting Guy! How can I determine if a computer has a removable drive attached to it and, if it does, copy a file to that drive? I have no way of knowing what the drive letter of that removable drive would be, assuming it even exists.
Hey, SN. You know, when you live in Seattle you don’t really need a calendar in order to know what time of year it is. For example, on Tuesday it rained all morning. Wednesday the Scripting Guy who writes this column had to scrape frost off his windows before he could drive to work. Today (Thursday) it’s raining once again. So, without looking at the calendar, what time of year must it be? You got it: it’s baseball season in Seattle!
Needless to say, the season is off to a smashing start. The Scripting Son’s high school team was supposed to begin play on Tuesday, but they were rained out. They rescheduled the game for Wednesday, and managed to squeeze it in, frost and all. They’re supposed to play again tonight, but a quick glance out the window suggests that the odds of that are pretty slim. If so, that would mean that the season started off with a bang: two rainouts in a span of three days.
And yes, that is impressive, but we’ve seen worse. For example, when the Scripting Son was a 13-year-old, his PONY League team had 11 of its first 13 games postponed due to rain. By the time the season reached its halfway point the team had played a grand total 5 of its scheduled 30 games.
Now that’s baseball season in Seattle.
Note. As a point of comparison, the Los Angeles Dodgers – who play 81 home games a year – have had a grand total of 17 rainouts since 1962. In fact, the Dodgers went nine years (April, 1967 to April, 1976) without having a single home game rained out.
Of course, many of you are probably wondering what the players do when a game gets postponed due to rain. Obviously we can’t speak for all the players. However, we can tell you that many of the players do the same thing that major league ballplayers do when a game gets rained out; they spend their time in the locker room writing scripts that can copy a file to a removable drive, even if they have no idea what the drive letter of that removable drive might be. For example, here’s a script that Indians outfielder Grady Sizemore wrote last season, when his team series with Seattle, which was supposed to be played in Cleveland, was snowed out:
Const OverwriteExisting = TRUE Set objFSO = CreateObject("Scripting.FileSystemObject") strComputer = "." Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2") Set colDrives = objWMIService.ExecQuery _ ("Select * From Win32_LogicalDisk Where DriveType = 2") For Each objDrive in colDrives strDrive = objDrive.DeviceID strTarget = strDrive & "\" objFSO.CopyFile "C:\Scripts\Test.txt", strTarget, OverWriteExisting Next
OK, let’s see if we can figure out how this works. (Yes, go ahead and brush the snow off of it first. We’ll wait.) As you can see, the script starts off by defining a constant named OverwriteExisting and setting the value to True. What’s this constant for? Well, suppose we’re going to copy a file named Test.txt to a removable drive, and suppose the removable drive already has a file named Test.txt on it. This constant tells the script to go ahead and replace the existing file with the new version of Test.txt.
After we define the constant our next step is to create an instance of the Scripting.FileSystemObject, the object we’ll use to copy the file from our local hard disk to the removable drive.
Speaking of which, we still have a problem here: we don’t even know if our computer has a removable drive, let alone know the drive letter for that drive. But that’s easy enough to find out. To get information about all the removable drives on the computer we connect to the WMI service on the computer. As soon as we’ve done that, we can use this line of code to return a collection of all the removable drives attached to that machine:
Set colDrives = objWMIService.ExecQuery _ ("Select * From Win32_LogicalDisk Where DriveType = 2")
What we’re doing here is querying the Win32_LogicalDisk class, asking to get back a collection of all the disk drives that have a DriveType equal to 2. You probably don’t know this, but a drive that has a DriveType equal to 2 is – oh, OK; apparently you do know this. Well, you’re right: a drive that has a DriveType equal to 2 is a removable drive.
You kind of stole our thunder there, didn’t you?
Our next step is to set up a For Each loop that will walk us through all the removable drives in the collection. What happens if our computer doesn’t have any removable drives? Nothing much; in that case the script will simply skip the For Each loop (because there’s nothing for it to walk through) and then coast to a stop.
But what happens if our computer does have a removable drive? In that case, we plunge headfirst into the For Each loop, pausing just long enough to assign the value of the drive letter (that is, the value of the DeviceID property) to a variable named strDrive:
strDrive = objDrive.DeviceID
That’s going to make strDrive equal to something like this: F:. This is useful information, but it’s not quite sufficient to copy a file to drive F. Instead, we have to add the trailing \ to the path, turning F: into F:\. That’s what this line of code is for:
strTarget = strDrive & "\"
Why do we have to do that? Well, the trailing \ tells the FileSystemObject that we want to copy a file to the container F:\; that is, we want to copy a file to the root folder on drive F. Suppose we tried to copy the file to F:, without the \. In that case, we’d get the following error message:
Microsoft VBScript runtime error: Permission denied
Why? Because without the \ on the end the FileSystemObject thinks we want to copy the file to drive F and to give the copied file the name F:. Needless to say, we can’t have a file named F:; we get the error message because the computer thinks we’re trying to replace all of drive F with this single file.
The computer doesn’t like it when we try to do things like that.
Once we add the trailing \, however, we can copy our file (C:\Scripts\Test.txt) to the removable drive by using the following line of code:
objFSO.CopyFile "C:\Scripts\Test.txt", strTarget, OverWriteExisting
Could we copy more than one file to the removable drive? Sure; just repeat the preceding line of code as many times as needed. That’s all there is to it. The best part, of course, is that we never had to know the drive letter of the removable drive. As long as the script knows the drive letter of the removable drive, well, that’s all that really matters.
Two quick notes about this script. First, it’s quite possible that a computer could have more than one removable drive connected to it. In that case, the script is going to copy Test.txt to each of those drives. (After we copy the file to the first drive in the collection the script will pop back to the top of our For Each loop and then copy the file to the next drive in the collection.) Is there any way to set things up so that the files get copied to only a particular removable drive? Well, maybe, but it wouldn’t be easy; that’s because removable drives generally don’t populate the Win32_LogicalDisk class with much information that would help you separate one drive from another.
Second, this script is designed to run on the local computer only; however, it can be modified to work against a remote computer provided that the file to be copied (C:\Scripts\Test.txt) resides on that remote computer. In other words, it’s pretty easy to copy a file from drive C of a remote machine to drive F on that same remote machine. How easy? Well, the following script (which we won’t explain today) will do the trick, copying a file from drive C on the computer atl-fs-001 to a removable drive attached to that same computer:
strComputer = "atl-fs-001" Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2") Set colDrives = objWMIService.ExecQuery _ ("Select * From Win32_LogicalDisk Where DriveType = 2") For Each objDrive in colDrives strDrive = objDrive.DeviceID strTarget = strDrive & "\" Set colFiles = objWMIService.ExecQuery _ ("Select * from CIM_DataFile where Name= 'C:\\Scripts\\Test.txt'") For Each objFile in colFiles strNewPath = strTarget & objFile.FileName & "." & objFile.Extension objFile.Copy(strNewPath) Next Next
That should do it, SN; if it doesn’t, please let us know. As for the Scripting Guy who writes this column, he plans to spend the rest of the day sitting around waiting for the phone to ring, and waiting for the Scripting Son to report that the game has been rained out. In case you’re wondering, no, you don’t have to wait with him; if you have other things to do, feel free to do them. And if you don’t have other things to do, here are several Seattle rain jokes to help you pass the time:
A newcomer first arrived in Seattle on a rainy day. When he awoke the next morning it was still raining. It continued to rain the next day, and the day after that. In desperation, he spotted a kid walking by and said, “Hey kid, does it ever stop raining around here?” “How should I know?” replied the kid. “I’m only 6.”
Well, maybe you’re right: one Seattle rain joke probably is enough, isn’t it? See you all tomorrow.
Editor’s Note: For those of you on the edge of your seats wondering whether the game was rained out: Yes, it was.