The other day I needed a script to look at all of the files on a share.  We had a file share that had grown significantly, and wanted to know why.  So I fired up Powershell, did a little googling, and had it working.  I am guessing that I will probably need this again at some point in the future, so I am going to document it here.

If you are not familar with Powershell, it is very powerful command-line shell that Microsoft has created.  If you are a heavy computer user and don’t like doing repetitive tasks, then Powershell could be very beneficial for you.  If you are a Windows administrator, then you really need to learn Powershell.  It is now in Microsoft’s Common Engineering Criteria, which means every server product that Microsoft introduces should support it.

For this issue, I started out with the Get-ChildItem cmdlet. This command is very similar to the DOS dir command, you can just use the command alone and it will give you a listing of the files and folders in you current directory. Or you can pass it a directory name as a parameter, and it will give you a list of the files and folders in that directory. For my example I am looking at the C:\Files directory, so my command is:

Get-ChildItem C:\Files

It returns:

Mode                LastWriteTime     Length Name
----                -------------     ------ ----
----         1/21/2010  10:25 PM            subfolder
-a---         1/21/2010  10:25 PM          0 file1.txt
-a---         1/21/2010  10:25 PM          0 file2.txt

This is a start, but it is not returning the files in the sub folders. Adding the recurse tag will also give us the files in the sub folders.

Get-ChildItem C:\Files -recurse

Returns:

Mode                LastWriteTime     Length Name

----                -------------     ------ ----

d----         1/21/2010  10:25 PM            subfolder

-a---         1/21/2010  10:25 PM          0 file1.txt

-a---         1/21/2010  10:25 PM          0 file2.txt

Directory: C:\Files\subfolder

Mode                LastWriteTime     Length Name

----                -------------     ------ ----

-a---         1/21/2010  10:25 PM          0 subfolderfile.txt

Not bad, but we really need the results formatted a little differently, and might want to see some different properties of the files.  So this brings up the question of what all properties are available.  You can get a list of all the properties that are available for a file at http://msdn.microsoft.com/en-us/library/system.io.fileinfo_members.aspx.  You can also use the Get-Member cmdlet, it will return all the properties of an item.  So we need to take one file, and then use the Get-Member cmdlet to get all of the properties for that file.  The following command will retrieve a single file and pipe it to the Get-Member cmdlet:

(Get-ChildItem C:\Files)[1] | Get-Member

For the problem that I was working on, I wanted the file name (including the path), the date it was created, and its size.  So the properties I want are FullName, CreationTime, and Length.  We can use the Select-Object cmdlet to return only these properties.  You pipe a item into the Select-Object cmdlet, and specify what properties you want to return.  So my script becomes:

Get-ChildItem C:\Files -recurse | Select-Object FullName, CreationTime, Length

And returns:

FullName                                CreationTime                            Lengt

--------                                ------------                            -----

C:\Files\subfolder                      1/21/2010 10:25:39 PM

C:\Files\file1.txt                      1/21/2010 10:25:18 PM                   0

C:\Files\file2.txt                      1/21/2010 10:25:18 PM                   0

C:\Files\subfolder\subfolderfile.txt    1/21/2010 10:25:49 PM                   0

This is getting a lot closer, but I would really like to see the file size in Mbytes, not bytes.  And I would like to see how many days old the file is.  Luckily we can specify formulas in the Select-Object cmdlet.  If I include these expressions my script becomes:

Get-ChildItem C:\Files -recurse | Select-Object FullName, CreationTime, @{Name="Mbytes";Expression={$_.Length / 1Mb}}, @{Name="Age";Expression={(((Get-Date) - $_.CreationTime).Days)}}

And returns:

FullName                      CreationTime                                         Mbytes                           Age

--------                      ------------                                         ------                           ---

C:\Files\subfolder            1/21/2010 10:25:39 PM                                     0                             0

C:\Files\file1.txt            1/21/2010 10:25:18 PM                                     0                             0

C:\Files\file2.txt            1/21/2010 10:25:18 PM                                     0                             0

C:\Files\subfolder\subfold... 1/21/2010 10:25:49 PM                                     0                             0

Now, I would like to exclude folders from the results.  For this we can use the Where-Object cmdlet.  You use the Where-Object cmdlet to specify the criteria for what is returned.  The file item and the folder item both have a property called PSIsContainer that specifies whether the item is a folder or not.  So I want to only include items where PSIsContainer is false.  My script becomes:

Get-ChildItem C:\Files -recurse | Where-Object {$_.PSIsContainer -eq 0} | Select-Object FullName, CreationTime, @{Name="Mbytes";Expression={$_.Length / 1Kb}}, @{Name="Age";Expression={(((Get-Date) - $_.CreationTime).Days)}}

And returns:

FullName                      CreationTime                                         Mbytes                           Age

--------                      ------------                                         ------                           ---

C:\Files\file1.txt            1/21/2010 10:25:18 PM                                     0                             0

C:\Files\file2.txt            1/21/2010 10:25:18 PM                                     0                             0

C:\Files\subfolder\subfold... 1/21/2010 10:25:49 PM                                     0                             0

You can use the Where-Object cmdlet to do whatever filtering you would like, for instance you could look at files that are over 5 Mb and where created in the past 7 days.  For my example I am happy to just return all files, however I would really like to be able to output this to a file, it would be really nice if I could open that file in Excel.  The Export-Csv cmdlet will accomplish this for me, I just need to pipe my command into the Export-Csv cmdlet, and specify a filename.  So my script becomes:

Get-ChildItem C:\Files -recurse | Select-Object FullName, CreationTime, @{Name="Mbytes";Expression={$_.Length/1Kb}}, @{Name="Age";Expression={(((Get-Date) - $_.CreationTime).Days)}} | Export-Csv c:\filelist.csv

Open the c:\filelist.csv file in Excel and you will see your list of files.  Powershell has a bit of learning curve, but once you have the basics of it down it can be a very powerful tool.  Just the items covered in this post will give you powerful tools for analyzing files.

Tags: