Summary: Microsoft Scripting Guy, Ed Wilson, shows how to use Windows PowerShell to detect and to fix files that have leading spaces in the file name.
Hey, Scripting Guy! I am hoping that you can help me. We have a rather strange problem at work, and if I do not get to the end of it, I am afraid I might be looking for a new job. The reason is that for some reason we are getting files that have a leading space in the file name on one of our storage servers. This is only part of the problem because our backup program is rather old. It works—except that it fails when it finds a file with a leading space in the file name. You might say we should upgrade our backup program, and that would be fine, except that our tape array is no longer supported and new backup programs do not have the correct driver. I know, I know, I know. But, for now, I need to figure out a way to detect files that are getting this leading space in the file name. As luck would have it, the way we found out that the files were not getting backed up is that our CEO corrupted one of the files that had the leading space in it, and when we attempted to restore the file, we discovered the problem. Of course, we did not replace the guy who was in charge of backups and who must have been the last planet to know about the software and the old tape array, but no one wants to hear about that. Well anyway, now I am “on notice” and I am really hoping you can help me.
—KS
Hello KS,
Microsoft Scripting Guy, Ed Wilson, is here. Wow! KS, I am sorry things have not been going so well for you recently. I am really saddened to hear about your problems at work. To be honest, I do not think I have ever created a file with a leading space in the name—don’t know why I would want to do so. As a matter of a fact, I am not certain that until I read your email to scripter@microsoft.com, I even knew that one could create a file with a leading space in it.
The first thing I decided to do was to create a file with a leading space in it. I opened Windows File Explorer, and created a new text file with 10 leading spaces. This is shown in the image that follows.
But when I pressed ENTER, it seems that Windows Explorer stripped out the leading spaces. This is shown in the image that follows.
Of course, the best way to check this is to use Windows PowerShell. To do this, I run the following Windows PowerShell code, which gets the length of the file name, and uses the Trim string method to remove excess spaces. Nothing returns, which lends credence to my suspicion.
PS C:\a> gci -Recurse | % { if($_.name.length -ne $_.name.trim().length) { "file with space"}}
KS, based on this easy test, it would seem that Windows File Explorer, at least, does not permit creating files with spaces at the beginning of the name. So someone is using some other method to create the files. If I am going to test my code, I need a quick easy way to create files with leading spaces. To do that, I need to use Windows PowerShell. I create a function called New-FilesWithLeadingSpaces. This function accepts three parameters. The first is the path to create the files, and the second is a base file name parameter. I set the default value to myfile.txt, but you could use any string you choose to do this. The final parameter is Count, which tells the function how many files to create. Inside the function, I have a for loop that counts up to the Count property. Inside the loop I create a space padding string that I prepend to each FileBaseName. Of course the ItemType is file. Here is the new New-FilesWithLeadingSpaces function:
Function New-FilesWithLeadingSpaces
{
Param([string]$path = "c:\test",
[string]$FileBaseName = "myfile.txt",
[int]$count)
For($i = 0; $i -le $count ;$i ++)
{ $pad = " " * $i
New-item -Path $path -Name "$pad$FileBasename" -ItemType file}
} #end function new-FileWithLeadingSpaces
When I run the script, I bounce over to the Windows File Explorer, and it is obvious that Windows does, in fact, permit files with a leading space—KS, a fact unfortunately of which you are painfully aware. The Windows File Explorer results are shown here:
So, I created a new function called Get-FilesWithLeadingSpaces. This function accepts a single parameter, Path, which specifies from whence the search begins. Next, the Get-ChildItem cmdlet with the –recurse switch is used to return the FileInfo objects that are piped to the Foreach-Object cmdlet. Inside the loop, the length of a file name is compared with the length of the file name after using the Trim method to remove any trailing spaces. Using the Trim string method is perfect for this application. The following illustrates using Trim. First, a variable that holds five spaces and the word fiveSpaces is created. That value displays and shows that there are five spaces preceding the value. Now, the Trim method is called to remove the spaces. This is shown here:
PS C:\a> $name = " fiveSpaces"
PS C:\a> $name
fiveSpaces
PS C:\a> $name.Trim()
fiveSpaces
If the length of the trimmed file name is not equal to the file name, it means that it contains either leading or trailing spaces. Therefore, the file name displays to the console. The complete function is shown here:
Function Get-FilesWithLeadingSpaces
{
Param([string]$path = "c:\a")
Get-ChildItem -Path $path -Recurse |
foreach-object {
if($_.name.length -ne $_.name.trim().length)
{ "$($_.basename) contains a leading space"} }
} #end function Get-FilesWithLeadingSpaces
When the function loads and is run, the output in the Windows PowerShell ISE appears as shown here:
If there was only one file, renaming it might not be too bad. But in the case of my example, if I renamed each file after removing the leading spaces, I would end up with a bunch of duplicate files. Therefore, I decided to add a counter number to the file name after renaming it. Renaming a file in Windows PowerShell is trivial because there is a Rename-Item cmdlet that takes a Path and a new Name parameter. Rather than creating a new function with much of the same capabilities of the Get-FilesWithLeadingSpaces function (such as detecting files that have a leading space in the name, I decided instead to add a Rename switch to rename the files with leading spaces in the name. Adding a switched parameter is super easy in Windows PowerShell—all you do is use the [switch] type constraint in front of the variable name as shown here:
[switch]$rename
Because I wanted an easy way to create a unique file name, I added a counter variable at the Begin portion of the Foreach-Object command. In the Begin portion, I set the value of the $count variable to 0. This appears here.
-Begin {$count = 0}
When I detect that I have a file name with spaces either before or after the file name, I check to see if the $rename switch appears. If it does, the Rename-Item cmdlet renames the file. The path is the FullName property from the FileInfo object. The FullName property contains the complete path to the file, and it should always be used when working with files that are discovered via the Get-Item cmdlet (this is one good property name to remember). Next, the new name needs to be created. First I take the BaseName property—that is the file name without the path or the file extension. I trim that BaseName, and then I use the count number from the $count variable. Lastly, I add back in the file extension. The file extension is always available from the Extension property. If at any time you are not certain of what properties you have available to you, use the Get-Member cmdlet to display the properties of your file. This technique is shown here:
PS C:\a> Get-Item .\myfile.txt | Get-Member -MemberType property
TypeName: System.IO.FileInfo
Name MemberType Definition
---- ---------- ----------
Attributes Property System.IO.FileAttributes Attributes {get;set;}
CreationTime Property System.DateTime CreationTime {get;set;}
CreationTimeUtc Property System.DateTime CreationTimeUtc {get;set;}
Directory Property System.IO.DirectoryInfo Directory {get;}
DirectoryName Property System.String DirectoryName {get;}
Exists Property System.Boolean Exists {get;}
Extension Property System.String Extension {get;}
FullName Property System.String FullName {get;}
IsReadOnly Property System.Boolean IsReadOnly {get;set;}
LastAccessTime Property System.DateTime LastAccessTime {get;set;}
LastAccessTimeUtc Property System.DateTime LastAccessTimeUtc {get;set;}
LastWriteTime Property System.DateTime LastWriteTime {get;set;}
LastWriteTimeUtc Property System.DateTime LastWriteTimeUtc {get;set;}
Length Property System.Int64 Length {get;}
Name Property System.String Name {get;}
The newly added rename clause appears here.
if($rename)
{
Rename-Item -Path $_.fullname -NewName ("{0}{1}{2}" -f `
$_.basename.trim(),$count,$_.extension)
$count++
}
The complete function is shown here:
Function Get-FilesWithLeadingSpaces
{
Param(
[string]$path = "c:\a",
[switch]$rename
)
Get-ChildItem -Path $path -Recurse |
foreach-object -Begin {$count = 0} -process {
if($_.name.length -ne $_.name.trim().length)
{
if($rename)
{
Rename-Item -Path $_.fullname -NewName ("{0}{1}{2}" -f `
$_.basename.trim(),$count,$_.extension)
$count++
}
else
{"$($_.basename) contains a leading space"}} }
} #end function Get-FilesWithLeadingSpaces
After I run the function, I go back into the Windows File Explorer to see if it worked. As shown here, it worked perfectly.
KS, that is all there is to using Windows PowerShell to detect files that have leading spaces. Join me tomorrow for more good Windows PowerShell stuff.
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