Last Update: Sep 04, 2024 | Published: Nov 05, 2014
Here’s another problem that recently came across my desk. The need was to identify files with long names. Even if you don’t have this need, these problem solving articles will hopefully demonstrate PowerShell techniques and concepts that you can use in other projects or tasks. With that in mind, let’s walk through the process. You can follow along typing commands in either the console or the PowerShell ISE.
To make our PowerShell code re-usable, let’s define a variable for a folder we want to check.
$path = "c:work"
You can verify by getting all files within that path.
dir $path -file -Recurse
We’re going to look at all files, but you could just as easily limit your search to files of a specific type or name. When approaching a PowerShell problem, it is helpful to look at a single, representative object to discover what you can use. I’m going to look at a single file.
PS C:> get-item C:workComputerData.xml | Select Name Name ---- ComputerData.xml
The name property is a string object that has a single property of length.
PS C:> $file = get-item C:workComputerData.xml PS C:> $file.name ComputerData.xml PS C:> $file.name.length 16
In PowerShell you can keep expanding properties like I’ve done here. But don’t confuse this property with the length property of the file itself, which is the file size. We can take the next step and use a custom property with Select-Object.
PS C:> get-item C:workComputerData.xml | Select Name,@{Name="NameLength";Expression={$_.name.length}} Name NameLength ---- ---------- ComputerData.xml 16
Now that we know how to get the value for one item, we can expand this and get it for all items. I’ll test using a subset of files.
dir $path -file -recurse | Select Name,@{Name="NameLength";Expression={$_.name.length}} -first 10
This command sends an object to the pipeline that we can sort.
dir $path -file -recurse | Select Name,@{Name="NameLength";Expression={$_.name.length}} -first 10 | Sort NameLength -Descending
Now that we have something that works for a sample, we can expand our search and get the top 10 longest file names in the specified path.
dir $path -file -recurse | Select Directory,Name,@{Name="NameLength";Expression={$_.name.length}} | Sort NameLength -Descending | Select -first 10
Even though we’ve been looking at file name we could extend the concept for full path.
dir $path -file -recurse | Select FullName,@{Name="NameLength";Expression={$_.fullname.length}} | Sort NameLength -Descending | Select -first 10
I created a test file and folder with a ridiculous length to demonstrate.
PowerShell almost always has alternatives. This is a good thing because you might have different requirements for how you want to use this information. Another technique is to add a custom property to the file object with the Add-Member cmdlet.
dir $path -file -recurse | Add-Member -MemberType ScriptProperty -Name NameLength -value {$this.name.length} -PassThru -force | Sort NameLength -Descending | Select -first 10
Add-Member is a very handy cmdlet and I encourage you to read the help and examples. In this particular scenario I’m defining a calculated property. The value will come from evaluating the script block. In this syntax you can use $this to reference the current object in the pipeline. Don’t use $_ or $psitem. You won’t get an error but you also won’t get a value. And be sure to use –Passthru if you are piping you command to another step as I’m doing. If you don’t use –Passthru, then the rest of the expression will not do anything since there will be nothing in the pipeline.
Using Add-Member makes the property part of the object, even if you don’t see it as part of the output. It is there, but you have to ask for it if you want it.
pdir $path -file -recurse | Add-Member -MemberType ScriptProperty -Name NameLength -value {$this.name.length} -PassThru -force | Sort NameLength -Descending | Select -first 10 -property Directory,Name,NameLength,CreationTime,LastWriteTime
Or maybe you want to filter on this new, custom property.
dir $path -file -recurse | Add-Member -MemberType ScriptProperty -Name NameLength -value {$this.name.length} -PassThru -force | where {$_.NameLength -ge 80 } | Select fullname
Another alternative is to use a custom sort property. Instead of using a property name, you can define a custom property using a hashtable defined like this:
@{Expression={ some scriptblock }}
The scriptblock can use $_ to reference the current object in the pipeline. Here’s how we could apply this to the problem at hand.
dir $path -file -recurse | Sort @{Expression={$_.name.length}} -Descending | Select -first 10
This approach still gives us the full file object, but there is no property to indicate the file name length. This is where alternatives are helpful. Do you only need to find file objects based on the length of the file name, or do you want to know the file name length as well?
The most important element in solving PowerShell problems is to start small and work your way out. If you tried to come up with my final command in your first attempt, you might have struggled. Start small and test with a subset of whatever it is you’re working with. Once the command works with 10 items, it should work with 100 or 1,000.