An extended look at Windows file management with PowerShell

Scripting expert Christa Anderson explains how to use Windows PowerShell to create folders in a temporary directory and populate them from a network share.

This month, I'm going to expand on our previous installment that dealt with basic file management using Windows PowerShell.

Periodically, I'm required to run a script that creates folders in a temporary directory and populates them from a network share. When I have the contents of this folder ready, I save them to a different network share. I need to run this script four times, so I can't have the originally-named folders remaining in my temporary directory. I rename them according to their contents. Unfortunately, manually finding, renaming and moving the folders is a real pain.

To do that programmatically, I'll need to do the following:

  • Find all the folders in my temporary directory
  • Rename them according to their contents
  • Copy them to their final location

We've been using the file system provider to show examples of how to use get-childitem, new-item and get-content. To fulfill my requirements for this project, we'll need to get a bit more in depth with object folders than we have before.

Finding the folders

I thought I could sort the contents of a folder (like my temporary folder) by type using get-childitem, then get the folders and then manipulate them. As it turns out, though, itemtype is not a supported parameter for get-item. (I wonder why? After all, itemtype is supported for the new-item cmdlet.) Obviously, I would have to go about things a little differently in order to find a way of retrieving folders, but not files.

The fundamental difference between folders and files is that folders can contain other objects and files cannot, so I need to look for things that are containers. When I find them, I will assign them to a variable so I can work with them later. But how do I phrase a query to return only containers? To do that, I need the right syntax. I'll get that by using get-member.

The function of get-member is to show you the properties and methods of different kinds of objects, since once you know the properties and methods you'll be able to manipulate an object. To find out the property of a folder so you'll know how to phrase a query to return only folders, plug in the folder where you are storing temporary files -- first assigning that location to a variable so it's easy to return to it:

$junkchild = get-childitem c:junk
$junkchild | get-member

When I run get-member, I get a list of properties, methods and noteproperties (PowerShell-specific fields). I scan through the list and… Eureka! There's a noteproperty called psIsContainer. That sounds like something that can determine whether I've got a container or not. Remember to keep these results handy -- they're going to be useful during the rest of this exercise. (If you ever want to know what a property contains, you can echo it to the screen using write-host.)

Now I'm ready to create a variable that finds all the folders in my temporary directory. Again, I'm storing the results of this search in a variable so it's easier to work with them later. The syntax here is pretty simple. Notice that the conditions for where are enclosed in curly brackets.

$junkall=get-childitem c:junk | where {$_.psIsContainer -eq $true}

When using get-member to discover the characteristics of an object that you want to reuse on other objects, here is one last thing to remember -- always be sure that the two objects really match. Otherwise, (a) you may not find the characteristics that you need and (b) your scripts may not work if the actual targets don't support the property or method you got while experimenting.

Rename the folders

As I said before, my script to copy files must run four times to get data from different parts of the internal network share. After I've run it the first time, I need to rename the destination folders created by this script according to their contents before I can run the script again.

To do that, I use rename-item just as before, but with a twist. The thing is, I don't want to completely rename the files; I want them to keep their original names but reflect how they're unique. Hence, I get the path (which I know is contained in the .FullName property that I got from get-member) and supply the new name, e.g., the original name combined with my identifying information.

As you'll recall, last month, when concatenating string and variable content with Windows PowerShell, you enclosed the whole thing in quotes. I didn't include the name of the property I was concatenating with "x86" because rename-item assumes I was talking about a name. If I'd typed –newname "x86$_.Name", my filenames would have looked like this: x86junk01.Name.

$junkall | foreach-object -process {rename-item -path $_.fullname -newname "x86$_"}

So far, we haven't used foreach-object. It basically does what it sounds like: For each object included in what you plug into it (e.g., a series of numbers, a registry key containing subkeys or, in this case, a directory containing subdirectories), it will do something. What it does depends on what you put in the curly brackets after the –process parameter -- in this case, renaming the folder.

Copy the folders to the network share

Finally, I will move the folders to the external network share. We used move-item last month. I'm going to use it here now, but instead of using the hard-coded source path, I'll get the source path using the properties from get-member (in this case, .FullName).

WARNING: To make this last piece work, you have to refresh the contents of $junkall so that it contains the new names. Otherwise, the cmdlet will fail because the variable references folders no longer exist.

$junkall | foreach-object -process {move-item -path $_.FullName -destination c:destjunk}

All together, the whole script for renaming and moving those files looks like this:

$junkall=get-childitem c:junk | where {$_.psIsContainer -eq $true}
$junkall | foreach-object -process {rename-item -path $_.fullname -newname "x86$_"}
$junkall=get-childitem c:junk | where {$_.psIsContainer -eq $true}
$junkall | foreach-object -process {move-item -path $_.FullName -destination c:destjunk}

This month I extended my discussion of the file system provider to help you learn more about the characteristics of file and folder objects that are available to you using get-member. With that information, use where to filter results according to those characteristics (dates, object types, names, etc.) and store the results in a variable. Finally, you can plug that variable into foreach-object and perform an action on all the objects retrieved through your original filtering.

Miss a column? Check out the Scripting School archive.

A Terminal Services MVP, Christa Anderson is a program manager on the Terminal Services team at Microsoft. She is an internationally known authority on scripting, the author of Windows Terminal Services, The Definitive Guide to MetaFrame XP, and co-author of the book Mastering Windows 2003 Server. If you have a scripting question for Christa, please email her at [email protected] She often uses these emails as fodder for her scripting columns.

Dig Deeper on Windows Server storage management