Files and Folders, Part 3: Windows PowerShell

By The Microsoft Scripting Guys

Sesame Script

Welcome to Sesame Script, the column for beginning script writers. The goal of this column is to teach the very basics of Windows scripting for system administration automation. We’ll provide you with the information you’ll need to begin reading and understanding scripts and to start modifying those scripts to suit your own needs. If there’s anything in particular about scripting you’re finding confusing, let us know; you’re probably not alone.

Check the Sesame Script Archives to see past articles.

Special Offer! Download all the Sesame Script columns (from the very first column through the June, 2007 edition) in one easy-to-read, fully-searchable .chm file.

On This Page

Back by Popular Demand
Files and Folders in Windows PowerShell
Check for Files and Folders
Copy Files and Folders
Move Files and Folders
Delete Files and Folders
Copy by Date
The End

When you hear “Back by popular demand,” have you ever wondered what “popular demand” is? Is the item or event in question really in demand? And just how popular is it? Maybe it’s just another way of saying “Back because we can’t think of anything else to do.”

Just something to think about.

While you’re thinking about that, we have a special treat for you in this month’s Sesame Script. Back by popular demand, it’s files and folders! Yes, that right. Two months ago we talked about working with files and folders locally using the FileSystemObject. Last month we talked about working with files and folders remotely using Windows Management Instrumentation (WMI). This month we have something a little different. Yes, we’re still talking about files and folders, but - instead of working with VBScript - we’ll be working with Windows PowerShell. This is a first (and quite possibly a last) for Sesame Script. Enjoy.

Files and Folders in Windows PowerShell

We’ll start out by telling you that everything you learned last month about manipulating files and folders with WMI will work in Windows PowerShell. PowerShell has a cmdlet called Get-WMIObject that allows you to access all the classes, methods and properties of WMI. Just like with VBScript, WMI is the only way to work with files and folders remotely in Windows PowerShell. We’re not going to get into working with WMI in Windows PowerShell in this article, but we’re going to show you the PowerShell way to work with files and folders.

Check for Files and Folders

Like many things in Windows PowerShell, checking for specific files and folders initially seems like a pretty simple task. For example, let’s say we want to see whether there are any files in the folder C:\Scripts. That really is simple:

$a = Get-ChildItem C:\Scripts
$a.Count

The first line uses the Get-ChildItem cmdlet to retrieve all the files in the folder C:\Scripts, then assigns the output (the list of files) to the variable $a. We then use the Count property to display the number of items (files) contained in $a. If there are more than zero items in $a, then obviously C:\Scripts contains files. We can go a step further and be a little more specific with our output:

$a = Get-ChildItem C:\Scripts
if ($a.Count)
{
    "C:\Scripts contains files"
}
else
{
    "C:\Scripts does not contain files"
}

Here we’re checking our Count property inside an If statement. If the Count is anything but zero (because zero is the same as False) we display the message “C:\Scripts contains files.” If the Count is zero (meaning the If statement is False) we fall into the Else portion of the statement and display the message “C:\Scripts does not contain files.”

That’s all very nice. But what if we want to check for a specific file or folder? Believe it or not, that’s even easier:

Test-Path C:\Scripts

This one simple command returns True if the folder C:\Scripts exists and False if it doesn’t. Here’s our spelled-out version:

if (Test-Path C:\Scripts)
{
    "C:\Scripts exists"
}
else
{
    "C:\Scripts does not exist"
}

That script shows us whether a folder exists, but what about a file? Well, it’s the same thing:

Test-Path C:\Script\Test.txt

We again use the Test-Path cmdlet, passing it the full path to the file we’re looking for. We don’t actually have to include the full path; if you want to see whether the file File1.txt is in the current directory you can simply use this command:

Test-Path File1.txt

Want to see if the file or the folder exists? Okay:

if (Test-Path C:\Scripts)
{
    if (Test-Path C:\Scripts\Test.txt)
    {
        "Folder and file exist"
    }
    else
    {
        "Folder exists, file doesn't"
    }
}
else
{
    "Folder does not exist"
}

We start by running the Test-Path cmdlet against the C:\Scripts folder. If Test-Path returns True, meaning C:\Scripts exists, we then move onto the next If statement, this time using Test-Path to check for the file, C:\Scripts\Test.txt:

if (Test-Path C:\Scripts\Test.txt)

If the file also exists, we display a message stating that fact. If the file doesn’t exist, well, we say that instead:

"Folder exists, file doesn't"

Suppose we ran the command Test-Path C:\Scripts and the folder didn’t exist; in that case, Test-Path would return False and we would display a message saying the folder doesn’t exist:

else
{
    "Folder does not exist"
}

The Test-Path cmdlet also allows you to look for files and folders that match a certain criteria by using wildcards. Here’s an example that checks to see whether there are any files in the C:\Scripts folder with a .txt file extension:

Test-Path C:\Scripts\*.txt

As you can see, we used the wildcard character (*) to mean “anything,” so we’re asking for anything in the C:\Scripts folder that starts with any character or set of characters and ends with .txt. If any .txt files exist in the folder, whether it’s one or a hundred, this command returns True.

Copy Files and Folders

Now that we know whether a file or folder exists, let’s move on to copying that file or folder to another location. We’ll start with a file:

Copy-Item C:\Scripts\test.txt C:\Test

Once again, this is pretty simple. Windows PowerShell provides a cmdlet named Copy-Item that copies a file or folder to another location. We pass two parameters to Copy-Item: the name of the file we want to copy (C:\Scripts\test.txt) and the location to which we want to copy it (C:\Test). And that’s all there is to it.

One thing to be careful of is that Copy-Item will overwrite an existing file. So if there is already a file named Test.txt in the C:\Test folder, the preceding command will simply write over it without even asking you about that. If you want to make sure you’re not overwriting anything, you might want to use Test-Path first, or use the -confirm parameter:

Copy-Item C:\Scripts\test.txt C:\Test -confirm

By adding the -confirm parameter you’ll be prompted to verify that you really want the copy operation to continue:

Confirm
Are you sure you want to perform this action?
Performing operation "Copy File" on Target "Item: C:\Scripts\test.txt Destination: C:\Test\test.txt".
[Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "Y"):

Of course, this might get a little inconvenient if you’re trying to copy multiple files or entire folders at once (unless you select [A] Yes to All at the prompt). And speaking of multiple files, here’s how you can copy all the files in the C:\Scripts folder to the C:\Test folder:

Copy-Item C:\Scripts\* C:\Test

Once again we’ve used the wildcard character to represent “anything,” or, in this case, “everything.” We’re saying copy all files and folders from C:\Scripts to C:\Test. This command copies the contents of C:\Scripts, but what if we wanted to copy C:\Scripts itself? In other words, let’s say C:\Scripts contains three files:

C:\Scripts\test.txt
C:\Scripts\file1.vbs
C:\Scripts\file1.ps1

After running the preceding command, C:\Test will look like this:

C:\Test
    test.txt
    file1.vbs
    file1.ps1

That’s nice, but what we really wanted was to copy the Scripts folder itself, so we’d have a final structure like this:

C:\Test
    \Scripts
        test.txt
        file1.vbs
        file1.ps1

That should be simple enough. Rather than using the wildcard to specify everything in C:\Scripts, we’ll just copy C:\Scripts:

Copy-Item C:\Scripts C:\Test

After running this command, our C:\Test folder looks like this:

C:\Test
    \Scripts

No, we didn’t forget anything there; that’s really all there is. We copied the Scripts folder, but none of the files came with it. We have an empty Scripts folder within our Test folder. That’s … nice. Not what we wanted, but nice. So how do we get what we want? By once again adding a parameter to our command, this time the -recurse parameter:

Copy-Item C:\Scripts C:\Test -recurse

This command recursively copies everything from C:\Scripts - including all the files and subfolders, along with the files and subfolders of the subfolders - to the C:\Test folder.

Move Files and Folders

This next part seems pretty obvious. If you use the Copy-Item cmdlet to copy files and folders, you probably use the Move-Item cmdlet to move files and folders, right? Right. (Isn’t it nice when things that seem so obvious really are that obvious?) Let’s move the file Test.txt from C:\Scripts to C:\Test:

Move-Item C:\Scripts\test.txt C:\Test

Just like with Copy-Item, we specify the path to the file we want to move (C:\Scripts\test.txt) and the folder to which we want to move it (C:\Test), and we’re done. After running this command Test.txt will no longer be located in the Scripts folder, but it will be in the Test folder instead.

One difference between Copy-Item and Move-Item is that by default, Move-Item won’t overwrite an existing file. If you already have a file name Test.txt in the Test folder, you’ll receive an error if you try to run the preceding command:

Move-Item : Cannot create a file when that file already exists.
At line:1 char:10
+ Move-Item  <<<< C:\Scripts\test.txt C:\Test

However, you can override the default behavior and force Move-Item to overwrite the file. You do this by adding the -force parameter:

Move-Item C:\Scripts\test.txt C:\Test -force

This parameter tells Move-Item to go ahead and move the file; we don’t care if we overwrite an existing file by that name that might be located in the destination folder.

We can also move the contents of entire directories:

Move-Item C:\Scripts\* C:\Test

This command moves all files and folders - and files and folders within those folders - from the Scripts folder to the Test folder. Want to move only the .txt files? No problem:

Move-Item C:\Scripts\*.txt C:\Test

Once again we’ve used the wildcard character to say we want to move everything in the Scripts folder that ends in .txt. And amazingly enough, that’s exactly what happens when you run this command.

Delete Files and Folders

We’ve now used the Copy-Item cmdlet to copy a file and Move-Item to move a file - let’s see, how would we delete a file? Nope, you’re wrong this time, it’s not Delete-Item, but it’s close: Remove-Item. The Remove-Item cmdlet will delete the specified file or folder:

Remove-Item C:\Test\test.txt

As you can see, all we have to do is pass the name of the file we want to delete to Remove-Item and that file will be deleted (removed). Just like with the other cmdlets we’ve looked at, we can also use wildcards to delete multiple files:

Remove-Item C:\Test\*.txt

In this command we’re deleting all the files in the C:\Test folder that end with a .txt extension.

One thing to note is that, by default, Remove-Item won’t remove read-only files. If you try to remove a read-only file you’ll receive an error message:

Remove-Item : Cannot remove item C:\test\test.txt: Not Enough permission to perform operation.
At line:1 char:12
+ remove-item  <<<< c:\test\test1.txt

When we say “by default” that implies that things don’t necessarily have to work that way, which in this case is true. If you want to delete a file no matter what, just tack the -force parameter onto your command:

Remove-Item C:\Test\test.txt -force

Now let’s delete a folder:

Remove-Item C:\Test

Running this command will delete all the contents of the C:\Test folder. No, wait, we take that back: running this command will delete all the contents of the C:\Test folder if that folder contains nothing but files. If the folder contains subfolders Windows PowerShell will display this message:

Confirm
The item at C:\test has children and the Recurse parameter was not specified. If you continue, all children will be
removed with the item. Are you sure you want to continue?
[Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "Y"):

Apparently deleting a folder full of files is okay, but Windows PowerShell want’s to make sure you’re aware of the fact that you have some subfolders in this folder, so you better make sure you know what you’re doing. If you’d like to bypass this message you can, again, tack on the -recurse parameter:

Remove-Item C:\Test -recurse

This command will delete all the contents of C:\Test, including subfolders and their contents.

Copy by Date

This really has been pretty easy so far, hasn’t it? All right, it’s time to get just a tiny bit more complicated. Let’s copy all files created more than one month ago from the C:\Scripts folder to the C:\Test folder. Here’s how to do that:

foreach ($i in Get-ChildItem C:\Scripts)
{
    if ($i.CreationTime -lt ($(Get-Date).AddMonths(-1)))
    {
        Copy-Item $i.FullName C:\Test
    }
}

This is a pretty short script but it can still be a little confusing, so let’s walk through it. We start with a foreach loop:

foreach ($i in Get-ChildItem C:\Scripts)

We’re going to be copying files from C:\Scripts based on creation date, so we need to retrieve each of the files from C:\Scripts and take a look at its creation date. We use the foreach loop to grab one file at a time. We find all the files by using the Get-ChildItem cmdlet:

Get-ChildItem C:\Scripts

This command returns all the files and folders in C:\Scripts. We look at each of these files and folders individually by using the foreach loop to move them, one at a time, into the variable $i:

foreach ($i in Get-ChildItem C:\Scripts)

The first time through the loop $i will reference the first file in the folder. At that point we go to the If statement:

if ($i.CreationTime -lt ($(Get-Date).AddMonths(-1)))

Within the If statement we first check the creation date of the current file. We do that by looking at the CreationTime property:

$i.CreationTime

We only want to copy files that are more than one month old, so we need to compare this creation date to today’s date minus one month. We do the comparison using the less than (-lt) operator:

if ($i.CreationTime -lt

Now we need to figure out what the date was one month ago. We do that using a combination of the Get-Date cmdlet and the AddMonths method:

$(Get-Date).AddMonths(-1)

The Get-Date cmdlet simply returns the current date and time. Notice that we put Get-Date in parentheses; that means we need the Get-Date cmdlet to retrieve a date object before we call AddMonths on that object. AddMonths takes one parameter: the number of months we want to add to the date. Because we’re looking for the date one month ago, we need to subtract a month, so we pass -1 to AddMonths. That give us the current date minus one month, which we compare to the creation date of the current file:

if ($i.CreationTime -lt ($(Get-Date).AddMonths(-1)))

If the creation date of the file is less than the date one month ago, meaning the file was created more than one month ago, we copy that file to the C:\Test folder:

Copy-Item $i.FullName C:\Test

We’ve used the Copy-Item cmdlet just like we saw earlier, but for the source (copy from) parameter we passed in $i.FullName. We know that $i represents the current file; the FullName property returns the full path to that file. After we copy the file we loop around, grab the next file in the folder (placing it in $i), and do our checks all over again. The loop continues until we’ve looked at all the files and folders in the C:\Scripts folder.

A couple of things to note on this. If we run this script as-is we’ll read through all the files and subfolders in C:\Scripts, but not the files and folders within those subfolders. To do that we need to add the -recurse parameter to the call the Get-ChildItem in our foreach loop:

foreach ($i in Get-ChildItem C:\Scripts -recurse)

The problem we ran into when we did that was that when we got to our Copy-Item command, we were copying all the files, even those from subfolders, into the root C:\Test folder, not into the new subfolders. We can solve this problem by checking paths and things like that, but that was getting a little complicated for Sesame Script. That’s something you might want to play around with on your own.

The End

Yes, this really is the end of the three-part Sesame Script series on Files and Folders. Don’t be sad; the next topic will be just as riveting and useful as this one has been. Yes, we know, that’s not saying much. Okay, we’ll try to make the next topic more riveting and useful, but we’re not going to make any promises.