Summary: Microsoft Scripting Guy, Ed Wilson, talks about using Windows PowerShell to set the time stamps on Microsoft Word document files.
Microsoft Scripting Guy, Ed Wilson, is here. It is Saturday here in Charlotte, and the sunshine continues to make its presence known. It seems that the sun gets up rather early during the summer, and retires in the evening rather late. It is just around 6:00 in the morning, and I am sitting on the lanai drinking a cup of English breakfast tea with a cinnamon stick and about a half spoonful of hibiscus flower in the tea. The hibiscus flower gives the tea a nice citrus flavor without all the carbs. I have one of my Windows 8 laptops with me in the swing and am checking my email. I really love Windows 8, and I cannot wait until I can buy the Scripting Wife a Microsoft Surface. She has been begging for one since she saw one at the Atlanta TechStravaganza. (United States FTC Disclaimer: I work for the Microsoft Corporation, and Microsoft provides me my laptop and the Windows 8 bits for my laptop. They did not build my lanai, nor do they provide me with English breakfast tea at home. However, when I am in the Microsoft office, they do provide me with English breakfast tea.)
The problem with file time stamps
A few months ago, I wrote a function that uses Windows PowerShell to modify file access time stamps. In the Hey, Scripting Guy! Blog where I discussed this technique, I envisioned creating a single DateTime object and using that to set all three of the file access time stamps. I even discussed my use case scenario, which was to use the technique as a sort of cheap versioning system while I was working on the Windows 7 Resource Kit.
This week, however, as I began working with the Word documents stored on my computer for all of the Hey Scripting Guy! Blogs I have written, I came up with another use for the function−to correct time stamps that are modified through various means (such as backup and restore from a network SAN and modifications created to Microsoft Word built-in properties). Luckily, all of my Hey, Scripting Guy! Blogs have a file name based on the date in which the blog appears. For example, the file for today’s Weekend Scripter is: WES-8-4-12.docx. I am assured the file names are consistent because I have a Windows PowerShell script that creates all of the 365 files I need each year (the script also creates folders for each week of the year, and folders for each year.
With a file name of WES-8-4-12 or HSG-8-2-12, it is really easy to find the date portion of the file name. All I need to do is to retrieve the item (by using Get-Item or Get-ChildItem). WhenI have the FileInfo object, I use the BaseName property to obtain only the file name without the path or the file extension. Therefore, in my example, HSG-7-23-12 returns the following.
PS C:\> $a = Get-Item C:\fso\HSG-7-23-12.docx
PS C:\> $a.basename
HSG-7-23-12
When I have a value such as HSG-7-23-12, I can easily convert that value to provide me with a DateTime object. To do this, I first remove the HSG- portion of the filename. This technique is shown here.
PS C:\> $a = Get-Item C:\fso\HSG-7-23-12.docx
PS C:\> $a.basename
HSG-7-23-12
PS C:\> $a.BaseName.Substring(4)
7-23-12
Now, all I need to do is to cast that value to a datetime object and I am finished. This appears here.
PS C:\> $a = Get-Item C:\fso\HSG-7-23-12.docx
PS C:\> $a.basename
HSG-7-23-12
PS C:\> $a.BaseName.Substring(4)
7-23-12
PS C:\> [datetime]$a.BaseName.Substring(4)
Monday, July 23, 2012 12:00:00 AM
The commands and the output associated with the commands are shown in the figure that follows.
Interestingly enough, it is possible to do this all on a single line. This technique is shown here.
PS C:\> [datetime](Get-Item C:\fso\HSG-7-23-12.docx).basename.substring(4)
Monday, July 23, 2012 12:00:00 AM
Expanding the technique a bit
Now that I know I can create a DateTime object from the base file name, it is a simple matter of replacing Get-ChildItem for Get-Item, and looping through the returned files. I talked about filtering files on Monday of this week and on Tuesday of the week.
Because Microsoft Word changed the file extension during the four years that I have been writing the Hey, Scripting Guy! Blog, I decided to use a wild card character in the extension property. To see how many of each type of files I have, I used the following command where I group by the extension. The command and the output associated with the command are shown here (gci is an alias for the Get-ChildItem cmdlet).
PS C:\> Get-ChildItem -Recurse -Path C:\data\ScriptingGuys -Include HSG*.doc*,WES*.doc* | group
extension
Count Name Group
—– —- —–
731 .docx {C:\data\ScriptingGuys\2008\hsg_11_17_08\HSG_QHF_…
359 .Doc {C:\data\ScriptingGuys\2009\HSG_10_12_09\HSG-10-1…
To pick up the date from the file names as they cross the pipeline, I use the Foreach-Object cmdlet and a version of the command I derived earlier in this blog. The command is shown here.
Get-ChildItem -Recurse -Path C:\data\ScriptingGuys -Include HSG*.doc*,WES*.doc* |
foreach-object {[datetime]$_.basename.substring(4)}
Adding in the function and changing all the time stamps
I decided to make a few changes to the Set-FileTimeStamps function from the previous blog posting. There are two reasons. The first is that I want to pass a System.Io.FileInfo object to the function instead of a path. Secondly, the Path parameter in the original function uses a string, and passing a FileInfo object to the function automatically casts the object to a string. When this happens, the $doc variable no longer contains a FileInfo object, but rather it contains a string. This generates an error stating that the object does not contain a property named CreationTime or any of the other properties. The modified Set-FIleTimeStamps function is shown here.
Function Set-FileTimeStamps
{
Param (
[Parameter(mandatory=$true)]
[system.io.fileinfo]$doc,
[datetime]$date = (Get-Date))
ForEach-Object {
$doc.CreationTime = $date
$doc.LastAccessTime = $date
$doc.LastWriteTime = $date }
} #end function Set-FileTimeStamps
The script parameters must reside at the top of the script. The parameters section has not changed from any of the other scripts used this week. It is shown here.
Param(
$path = “C:\fso”,
[array]$include = @(“HSG*.docx”,”WES*.docx”))
The entry point to the script, accepts the command line parameters, and uses Get-ChildItem to find all of the files. It then walks through the collection of FileInfo objects, creates the time stamp from the file name, and calls the Set-FileTimeStamps function. This is shown here.
## entry point to script
$docs = Get-childitem -path $Path -Recurse -Include $include
Foreach($doc in $docs)
{
Try
{
$date = [datetime]$doc.BaseName.Substring(4)
Set-FileTimeStamps -Doc $doc -date $date
}
Catch [System.Exception] {“Error occurred setting date for $doc”}
}
When the script runs, it finds all of the appropriate files and makes the requisite changes to the file time stamps. One such file is shown in the image that follows.
I uploaded the complete script to the Scripting Guys Script Repository.
Well, that is all for today. Join me tomorrow when I have a new PowerTip and I introduce the “love-o-matic.” It is cool, and is the latest Scripting Guys “o-matic” tool.
I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.
Ed Wilson, Microsoft Scripting Guy
0 comments