To limit Get-ChildItem
's output to directories only, use the -Directory
switch (PSv3+; in PSv2, pipe to Where-Object { $_.PSIsContainer }
Apart from that, your solution should work, but doesn't due to a conceptual flaw in .NET's System.IO.DirectoryInfo.MoveTo()
method (and also System.IO.Directory.Move()
method), which PowerShell's Rename-Item
cmdlet builds on, as of .NET Core 3.0 preview6 / .NET 4.8:
The method doesn't recognize case variations of directory name as a different name, and fails with Source and destination path must be different
.
- Curiously, files are not affected, as you've experienced.[1]
Of course, while NTFS is case-insensitive, it is also case-preserving, so it should be possible to rename foo
to FOO
, for instance.
Workaround (PSv4+):
Note: js2010's helpful answer offers another, workaround, based on calling cmd.exe
for each input folder to use its ren
command. While it is conceptually simpler, the caveat is that this approach of creating a child process for every folder processed is inefficient and slow. That said, for occasional renaming operations that probably won't matter.
$path = 'C:\Users\Xyz\Desktop\sample'
Get-ChildItem -LiteralPath $path -Recurse -Directory |
Where-Object { $_.Name -cne $_.Name.ToUpper() } -PipelineVariable dir |
Rename-Item -PassThru -NewName { [IO.Path]::GetRandomFileName() } |
Rename-Item -NewName { $dir.Name.ToUpper() }
The workaround temporarily renames matching folders to a transient, randomly generated name ([IO.Path]::GetRandomFileName()
) and then applies the all-uppercase version of the original name.
The input folder's original state and name are captured via variable $dir
, which is "stashed" away for later use via the common -PipelineVariable
parameter.
[1] Caveat: When you use Get-ChildItem
or Get-Item
to report a specific directory or file by its literal name, whatever case variation you specify is reported back, even though the true case variation as stored in the filesystem may differ (File Explorer, by contrast, always shows you the true case). To see the true case, use
Get-ChildItem <parentDir> -Filter <name>
or, as a quick workaround that may show additional items, however, append *
to the name/path. In both cases, the name is treated as a wildcard pattern, and the result of the matching process reflects the true case.