How to make one command out of two - powershell

I'm searching a directory for files that have pieces of a name in the files. How can I make one command out of the two I have?
get-childitem "\\myfileserver\out\*" | foreach { rename-item $_ $_.Name.Replace("_test_me.", ".") }
get-childitem "\\myfileserver\out\*" | foreach { rename-item $_ $_.Name.Replace(".vfmpclmadj.", ".test.") }
Running the commands work but I would like to clean it up a little with only needing one command.

You could chain the Replace-Calls
get-childitem "\\myfileserver\out\*" | foreach { rename-item $_ $_.Name.Replace("_test_me.", ".").Replace(".vfmpclmadj.", ".test.") }

"*" is not necessary into your get-childitem command path
you can specify -file argument for take only file (better performance)
you can specify a clause where for rename only file with your patterns (better performance and why rename allwhen we can rename only necessary)
not necessary to use foreach, pipe work with rename-item command
like this:
Get-ChildItem "\\myfileserver\out" -file |
where {$_.Name -like "*_test_me.*" -or $_.Name -like "*.vfmpclmadj.*"} |
rename-item -NewName {$_.Name.Replace("_test_me.", ".").Replace(".vfmpclmadj.", ".test.")}

Related

Bulk rename files with only one common factor in the middle with CMD or Powershell

Okay so a bit of a weird one but I have files with names like file_ytd.test.dds, file2_ytd.test.dds, file3_ytd.test.dds, file_ytd.notatest.dds
and I want to rename them so that they are
test.dds, notatest.dss
and of course it removes the duplicates I wanted to use this command in powershell, but I would need to manually type out the full name for each "file_ytd." type, and I have about 100k different variations in 400k files
``` Dir | rename-item -NewName {$_.name -replace "file_ytd." ""} ```
What would be the best way about this?
If I'm understanding correclty, t his would do the trick.
-WhatIf parameter added to both Rename-Item and Remove-Item so you can see if it's doing what you want.
Get-ChildItem |
select fullName , #{N='NewName';E={$_.BaseName.Split('.')[-1]+$_.Extension}} |
group NewName | foreach {
Rename-Item -Path $_.Group[0].FullName -NewName $_.Name -WhatIf
If ($_.Count -gt 1)
{
$_.Group[1..($_.Count -1)].FullName | Remove-Item -WhatIf
}
}

How to rename multiple files in powershell from parent folder?

Hello I would like to know is there a way to replace multiple filenames from parent folder?
I was able to rename multiple files using command below, but I have to access each folder first to rename multiple files.
dir .\* -include ('*.mp4','*.srt') | Rename-Item -NewName { $_.Name -replace '\[','' -replace '\]','' }
I was trying to replace dir .\* to dir .\**\* to select from parent folder but didn't work.
What am I missing?
You can do that by adding the -Recurse switch to Get-ChildItem (dir is alias to Get-ChildItem).
When searching for files that contain square brackets, you need to use the -LiteralPath parameter.
Also, be aware that there is a snag if you pipe the results from that directly to Rename-Item..
When doing so, Get-ChildItem may pick up the already processed files again, wasting time of course, so to prevent that you can either do:
$rootFolder = 'X:\WhereTheFilesAre'
(Get-ChildItem -LiteralPath $rootFolder -Include '*.mp4','*.srt' -File -Recurse) |
Rename-Item -NewName { $_.Name -replace '\[|]' }
or
$rootFolder = 'X:\WhereTheFilesAre'
$files = Get-ChildItem -LiteralPath $rootFolder -Include '*.mp4','*.srt' -File -Recurse
foreach ($file in $files) {
$file | Rename-Item -NewName { $file.Name -replace '\[|]' }
}
In additional to marsze's concise answer and realizing this isn't really a performance question:
-Include is much slower than -Filter because it filters after retrieving from the file system. Whereas, -Filter has the file system do the heavy lifting. In essence -Filter is a nuanced version of moving filtering operations left in the command/pipeline to improve performance. However, -Filter doesn't take multiple values! That said, you may still be able to exploit this characteristic using a loop, something like:
'*.mp4','*.srt' |
ForEach-Object{
Get-ChildItem .\ -Filter $_ -File
} |
Rename-Item -NewName { $_.Name -replace '\[|\]' }
-File works even though you are using -Recurse and may carry a modest performance improvement. I also shortened the -replace to 1 operation which can only help further.
What you probably want is the -Recurse switch:
dir "the folder" -Recurse -Include ('*.mp4','*.srt')
Note that this will recurse all levels of subdirectories.
I just found out I could use dir . to select all files (with specific file types), including files in subdirectories.
dir . -Recurse -Include ('*.srt', '*.mp4') | Rename-Item -NewName { $_.Name -replace '\[|\]','' }

How to Rename Using Powershell

How to rename bunch of files in windows using powershell.
Example of filenames:
Image001 L#ter
Image002 L#ter
I have tried these two commands ,
get-childitem * | ForEach { Move-Item -LiteralPath $_.name $_.name.Replace("L#ter","")}
get-childitem * | ForEach { rename-item $_ $_.Name.Replace("L#ter","") }
I expect the output to be as Image001,Image002
Your question says to rename, but your code samples are using the Move-Item command. I am going to assume you were unsure hot to rename them correctly as you weren't actually moving them.
Get-ChildItem "C:\Temp\" -File | ForEach-Object {
Rename-Item -Path $_.FullName -NewName "$($_.Name -replace '\s*(l#ster)')"
}
The \s* will match any whitespace before your main capturing group
The (l#ster) is the main capture group, it looks for that exact phrase and will match it.
This is a frequent category of question. I like to use rename-item with a scriptblock. Take off the -whatif if it looks right. I'm assuming you don't want the space at the end of the names.
ls '* l#ter' | rename-item -newname { $_.name -replace ' l#ter' } -whatif
first, some small details about your commands:
get-childitem * | ForEach { Move-Item -LiteralPath $_.name $_.name.Replace("L#ter","")}
LiteralPath is meant to be used used exactly as it is typed. So I would use it with
$.FullName instead of $.name if I must use strange paths (like network shares).
second:
In the get-help for both move-item and rename-item I can see that the -path parameter :
Required? true
Position? 0
Default value None
Accept pipeline input? True (ByPropertyName, ByValue)
which means that we can pipe a collection of objects into it and the rename cmdlet will automatically pass through the collection :
Get-ChildItem -Path 'c:\tests\' -File -Recurse | Rename-Item -NewName ($_.name -replace ' l#ter') -Force -WhatIf
I have made this reply redundant for Drew's and Js2010's replys, but, as I am a beginner in powershell, I find easier to understand the answers with full commands.

error from Powershell when try to rename with sequential prefix *recursively*

I have no problem adding sequential prefixes to filenames. The following works great on the top directory in question.
$path="E:\path\newtest1"
$count=4000
Get-ChildItem $path -recurse | Where-Object {!$_.PSIsContainer -and $_.Name -NotMatch '^\d{4}\s+'} | ForEach -Process {Rename-Item $_ -NewName ("$count " + $_.name -f $count++) -whatif}
BUT if there are files in subfolders within the top directory, these are all completely missed. Whatif reports that for any deeper file it "does not exist".
I have tried the following, based on looking at some pages on other recursion problems, but as you can probably guess I have no clue what it is doing. Whatif shows that it does at least pickup and rename all the files. But the following does it too much and makes multiple copies of each file with each number:
$path="E:\path\newtest1"
$count=4000
Get-ChildItem -recurse | ForEach-Object { Get-ChildItem $path | Rename-item -NewName ("$count " + $_.Basename -f $count++) -whatif}
Really keen to get some guidance on how to get the first of these two snippets to work to find all files in all subdirectories and rename them with sequential number prepended.
Try it like so:
Get-ChildItem $path -recurse -file | Where Name -NotMatch '^\d{4}\s+' |
Rename-Item -NewName {"{0} $($_.name)" -f $count++} -whatif
When you supply $_ as an argument (not a pipeline object), that gets assigned to the Path parameter which is of type string. PowerShell tries to convert that FileInfo object to a string but unfortunately the "ToString()" representation of files in nested folders is just the filename and not the full path. You can see this by executing:
Get-ChildItem $path -recurse -file | Where Name -NotMatch '^\d{4}\s+' | ForEach {"$_"}
The solution is either to A) pipe the object into Rename-Item or B) use the FullName property e.g. Rename-Item -LiteralPath $_.FullName ....

Rename files to lowercase in Powershell

I am trying to rename a bunch of files recursively using Powershell 2.0. The directory structure looks like this:
Leaflets
+ HTML
- File1
- File2
...
+ HTMLICONS
+ IMAGES
- Image1
- Image2
- File1
- File2
...
+ RTF
- File1
- File2
...
+ SGML
- File1
- File2
...
I am using the following command:
get-childitem Leaflets -recurse | rename -newname { $_.name.ToLower() }
and it seems to rename the files, but complains about the subdirectories:
Rename-Item : Source and destination path must be different.
I reload the data monthly using robocopy, but the directories do not change, so I can rename them by hand. Is there any way to get get-children to skip the subdirectories (like find Leaflets -type f ...)?
Thanks.
UPDATE: It appears that the problem is with files that are already all lower case. I tried changing the command to:
get-childitem Leaflets -recurse | if ($_.name -ne $_name.ToLower()) rename -newname { $_.name.ToLower() }
but now Powershell complains that if is not a cmdlet, function, etc.
Can I pipe the output of get-childitem to an if statement?
UPDATE 2: This works:
$files=get-childitem Leaflets -recurse
foreach ($file in $files)
{
if ($file.name -ne $file.name.ToLower())
{
rename -newname { $_.name.ToLower() }
}
}
Even though you have already posted your own answer, here is a variation:
dir Leaflets -r | % { if ($_.Name -cne $_.Name.ToLower()) { ren $_.FullName $_.Name.ToLower() } }
Some points:
dir is an alias for Get-ChildItem (and -r is short for -Recurse).
% is an alias for ForEach-Object.
-cne is a case-sensitive comparison. -ne ignores case differences.
$_ is how you reference the current item in the ForEach-Object loop.
ren is an alias for Rename-Item.
FullName is probably preferred as it ensures you will be touching the right file.
If you wanted to excludes directories from being renamed, you could include something like:
if ((! $_.IsPsContainer) -and $_.Name -cne $_.Name.ToLower()) { ... }
Hopefully this is helpful in continuing to learn and explore PowerShell.
Keep in mind that you can pipe directly to Rename-Item and use Scriptblocks with the -NewName parameter (because it also accepts pipeline input) to simplify this task:
Get-ChildItem -r | Where {!$_.PSIsContainer} |
Rename-Item -NewName {$_.FullName.ToLower()}
and with aliases:
gci -r | ?{!$_.PSIsContainer} | rni -New {$_.FullName.ToLower()}
There are many issues with the previous given answers due to the nature of how Rename-Item, Piping, Looping and the Windows Filesystem works. Unfortunatly the the most simple (not using aliases for readability here) solution I found to rename all files and folders inside of a given folder to lower-case is this one:
Get-ChildItem -Path "/Path/To/You/Folder" -Recurse | Where{ $_.Name -cne $_.Name.ToLower() } | ForEach-Object { $tn="$($_.Name)-temp"; $tfn="$($_.FullName)-temp"; $nn=$_.Name.ToLower(); Rename-Item -Path $_.FullName -NewName $tn; Rename-Item -Path $tfn -NewName $nn -Force; Write-Host "New Name: $($nn)";}
slight tweak on this, if you only want to update the names of files of a particular type try this:
get-childitem *.jpg | foreach { if ($_.Name -cne $_.Name.ToLower()) { ren $_.FullName $_.Name.ToLower() } }
this will only lowercase the jpg files within your folder and ignore the rest
You need to temporarily rename them to something else then name them back all lower case.
$items = get-childitem -Directory -Recurse
foreach ($item in $items)
{
if ($item.name -eq $item.name.ToLower())
{
$temp = $item.FullName.ToLower() + "_"
$name = $item.FullName.ToLower()
ren $name $temp
ren $temp $name
}
It's more idomatic in PowerShell to use where instead of if in a pipeline:
gci -Recurse Leaflets |
? { $_.Name -ne $_.Name.ToLower()) } |
% { ren -NewName $_.Name.ToLower() }
A small but important correction to the answer from Jay Bazuzi. The -cne (case sensitive not equal) operator must be used if the where-part should return anything.
Additionally I found that the Path parameter needed to be present. This version worked in my setup:
gci -Recurse |
? { $_.Name -cne $_.Name.ToLower() } |
% { ren $_.Name -NewName $_.Name.Tolower() }
for everyone who is following this thread; the following line can also be used to lower both files and directories.
Get-ChildItem -r | Rename-Item -NewName { $_.Name.ToLower().Insert(0,'_') } -PassThru | Rename-Item -NewName { $_.Name.Substring(1) }
Main post: https://stackoverflow.com/a/70559621/4165074