Reputation: 13
I have a lot of folders with image files that accidentally saved without extension. I've managed to work out how to add the extension:
ls -file | ? {$_.extension -eq ''} | % {ren $_ ($_.name + '.jpeg')}
... but I can't work out how to make it apply to all subfolders.
This command doesn't work:
Get-ChildItem -Recurse ls -file | ? {$_.extension -eq ''} | % {ren $_ ($_.name + '.jpeg')}
any ideas where I'm going wrong, please?
Upvotes: 1
Views: 48
Reputation: 439832
Mathias' helpful answer explains the problem well and offers an effective solution.
Rename-Item
with a script block ({ ... }
) passed to -NewName
is an instance of a delay-bind script block.To add to it with a performance improvement:
In order to recursively find all extension-less files, you can replace:
Get-ChildItem -Recurse -File | ? {$_.extension -eq ''}
with:
Get-ChildItem -Recurse -File -Filter *.
which performs much better, because filtering happens at the source (low-level file-system APIs) rather than after the fact via Where-Object
(whose built-in aliases are ?
and where
).
Note:
*.
to match only extension-less files relies on legacy quirks of the -Filter
argument.
-Filter
wildcard syntax is not the same as PowerShell's wildcard expressions; notably, *.
would not work with Get-ChildItem
's -Include
/ -Exclude
parameters, which support only PowerShell wildcards.While helpful in this case, the inconsistencies of the wildcard syntax supported by -Filter
are potential pitfalls; notably, -Filter *.*
does not limit matches to files with an extension; it matches all files, and is effectively redundant.
*.*
sensibly does match only files with an extension, but there are caveats:
-Include
and -Exclude
comes with its own pitfalls - see the bottom section of this answer.Where-Object
approach, filtering is performed after the fact (all files are enumerated and then filtered), it is much slower than -Filter
use.The bottom line with respect to filtering with Get-ChildItem
:
Use -Filter
for best performance, but beware its quirks, see this answer for an overview.
Use -Include
if you need to support multiple patterns and/or quirk-free syntax and/or extended features, namely the use of [...]
for character sets and ranges, but beware the counterintuitive behavior when -Recurse
is not used - see the bottom section of this answer.
Use Where-Object
for post-filtering, if you need to perform more sophisticated filtering than wildcard expressions can provide; inside the script block passed to Where-Object
, you can use regexes for matching, for instance.
Upvotes: 0
Reputation: 174825
First, make sure to remove ls
from Get-ChildItem -Recurse ls -file
- ls
is an alias for Get-ChildItem
, and since you've already specified the cmdlet name PowerShell will attempt to locate a folder named ls
and start the search from there.
Second, instead of passing $_
to Rename-Item
(alias ren
) as a positional argument, pipe it:
Get-ChildItem -Recurse -File | ? {$_.extension -eq ''} | Rename-Item -NewName { $_.Name + '.jpeg' }
Piping the whole file info object to Rename-Item
allows PowerShell to bind the correct absolute path to the file system object, and it'll thus start working for files in nested folders.
Upvotes: 2