PowerShell Rename file name and keep extension - powershell

I have found code on here which does what I need it to it
$files = gci -filter "*.txt" |select fullname
foreach ($file in $files) {
$filename = $file.fullname
$newFilename = $filename.Replace(".", " ")
rename-item $filename $newFilename
}
I require the code to change the file name and keep the file extension. The code above replaces "." with " ". I can code it to rename the file again to add the extension back. Just wanted to see if there was a better way to do it.
Cheers

Change 'old' & 'new' to whatever you want
Remove the -Whatif to apply the command.
Get-ChildItem -Path C:\temp\files -Filter *.txt |
Rename-Item -NewName {$_.Basename.Replace("old","new") + $_.extension} -WhatIf -Verbose

If you are already in the directory where you want to rename all the files regardless of their extensions, this should help.
Dir | foreach {$i=1}{Rename-Item $_ -NewName (('{0:D5}'+$_.Extension) -f $i++)}
Where the first block after foreach works as an initializing block(if you skip it, tha value will start from default value 0), {0:D5} stands for first parameter with 0 padded up to 5 spaces. You could also write it like {0:00000} but that D is just shorter for decimal values. Needless to say, if you don't want any padding just use {0}.
P.S.: You will probably need to add the -recurse parameter (after Dir if you want to rename files in any subfolder of the directory as well. You can also use a -whatif parameter at the end of the code to see the preview of the changes.

Related

Is there a way to add a postfix to this script?

So I asked here before about helping with a script to copy and paste files from one folder to another.
However, after I was done, I found that some of the files went missing. I had 600,393 files but when I checked my new folder it only had 600,361 files.
I think it may have been overwritten by duplicates even though the naming convention was supposed to stop those kinds of problems.
Here's the script
$destfolder = '.\destfolder\'
Get-ChildItem -Recurse -File .\srcfolder\ |
Invoke-Parallel {
$_ | Copy-Item -Destination (
Join-Path $using:destfolder ($_.directory.parent.name, $_.directory.name, $_.name -join '-')
) -Verbose -WhatIf
}
(Thanks to the great dudes on r/software, r/Techsupport, and mklement0)
So is there a way to add a postfix that adds a 0 to the name of any file that has the same name as a file already in a folder?
like directory-subdirectory-0-filename.ext
EDIT- Problem is all the files are read-only not hidden, I don't want any hidden files.
Note that Get-ChildItem doesn't include hidden items by default, which may explain at least part of the the discrepancy.
Use the -Force switch to include hidden items.
Separately / additionally, you can deal with name collisions as follows:
$destfolder = '.\destfolder\'
Get-ChildItem -Force -Recurse -File .\srcfolder\ |
Invoke-Parallel {
$newName = Join-Path $using:destfolder ($_.directory.parent.name, $_.directory.name, $_.name -join '-')
# Try to create the file, but only if it doesn't already exist.
if ($null -eq (New-Item $newName -ErrorAction SilentlyContinue)) {
# File already exists -> create duplicate with GUID.
$newName += '-' + (New-Guid)
}
$_ | Copy-Item -Destination $newName -Verbose
}
Note:
With multi-threaded execution, assigning sequence numbers to duplicates would be a nontrivial undertaking, as each thread would have to "reserve" a sequence number and ensure that no other thread claims it before copying to a file incorporating this number is complete.
To avoid such challenges, the above approach simply appends a - plus a GUID to the target file name.

Powershell script to read and split filename from a folder

I'm new to powershell. I want to read names of each file in a folder for eg. 900_CA_2022.pdf, remove the _ from the filename and create a new text file which has name 900CA2022900_CA_2022.txt
Basically, I want to remove the _ from the extension-less file name, append the latter as-is, and use a new extension, .txt
Easiest way is use the same name of source file + .txt for create the text files without doing much damage to the source filenames.
E.g.
900_CA_2022.pdf -----> 900_CA_2022.pdf.txt
My alternative solution
Initial files
Script Code
$files = Get-ChildItem "./*.pdf" -Recurse -Force
$files | ForEach-Object{
New-Item -Path "$($_ | Split-Path)/$($_ | Split-Path -Leaf).txt" -Force
}
Result files
Update:
The stated requirements are unusual, but the next section provides a solution to address them.
The fact that you later accepted Joma's answer indicates that simply appending .txt to each input file name is what you actually needed; this is most easily accomplished as follows:
Get-ChildItem -Filter *.pdf | New-Item -Path { $_.FullName + '.txt' } -WhatIf
Note: The -WhatIf common parameter in the command above previews the operation. Remove -WhatIf once you're sure the operation will do what you want.
Important: All solutions below create the new files in the current directory. If needed, construct the target file path with an explicit directory path, using Join-Path, e.g.:Join-Path C:\target (($_.BaseName -replace '_') + $_.BaseName + '.txt')
To create new, empty files whose names should be derived from the input files, use New-Item:
Get-ChildItem -Filter *.pdf |
New-Item -Path { ($_.BaseName -replace '_') + $_.BaseName + '.txt' } -WhatIf
Note: If the target file exists, an error occurs. If you add -Force, the existing file is truncated instead - use with caution.
$_.BaseName is the input file's name without the extension.
-replace '_' removes all _ chars. from it.
To create new files whose names should be derived from the input files and fill them, use ForEach-Object:
Get-ChildItem -Filter *.pdf |
ForEach-Object {
# Construct the new file path.
$newFilePath = ($_.BaseName -replace '_') + $_.BaseName + '.txt'
# Create and fill the new file.
# `>` acts like Out-File. To control the encoding, use
# something like `| Out-File -Encoding utf8 $newFilePath` instead.
"content for $newFilePath" > $newFilePath
}
Note that > / Out-File and Set-Content (for string data) all quietly replace the contents of an existing target file.

How can I modify this PowerShell script to include changing directory names to lower?

I am trying to rename all files and directories to lower and I found a powershell script here: Rename files to lowercase in Powershell
My favorite answer is the following because it is the cleanest and most concise answer. However, it does not include directory names and I don't have enough rep yet to respond to the comment
Get-ChildItem -r | Where-Object { !$_.PSIsContainer } | Rename-Item -NewName { $_.FullName.ToLower() }
I don't know PowerShell and I don't intend to become proficient, please skip all the details I'm just looking for code to rename all my files and directories to lower and I don't need to know anything about how it works. I don't like the following solution because 1, it is too wordy and 2, it only does directory names and not file names.
Where-Object { $_.PSIsContainer -And $_.Name -CMatch "[A-Z]" } |
ForEach-Object {
$NName = $_.Name.ToLowerInvariant()
# Set temporary name to enable rename to the same name; Windows is not case sensitive
$TempItem = Rename-Item -Path $_.FullName -NewName "x$NName" -PassThru
Rename-Item -Path $TempItem.FullName -NewName $NName
}
I want one clean command to rename files and directories, similar to the first example, please
when i first wrote this, i just wanted to open powershell and paste a command. in hindsight, that is not most efficient way either. so i ended up saving each script (one for files, one for folders) into one .ps1 file that you put in whatever directory you want to lower, then right-click and "run with powershell" and it will rename all files and subdirectories
the script looks like this:
# files to lower
Get-ChildItem -r | Where-Object { !$_.PSIsContainer } |
Rename-Item -NewName { $_.FullName.ToLower() }
# folders to lower
$fso = New-Object -ComObject Scripting.FileSystemObject
Get-ChildItem . -rec -dir |
ForEach-Object { $fso.MoveFolder($_.fullname, $_.Fullname.ToLower()) }
as you mentioned on your provided code, Windows is not a case sensitive OS, so you need to rename the directories to a temp name (for example insert a character after lowering it) then rename it again (by removing the inserted character)
i modified your line as follow to be able to lower both directories and files, please give it a try
Get-ChildItem -r | Rename-Item -NewName { $_.Name.ToLower().Insert(0,'_') } -PassThru | Rename-Item -NewName { $_.Name.Substring(1) }

Reset Filenumber for new folder in Recurse Filerename Powershell

I am working on a powershell batch file.
I want to rename all the files in a certain amount of folders. I need to rename it to a certain part of the folder name followed by an incremental number. So far i managed to create a rename command in powershell that also add numbers to the files.
Get-ChildItem -recurse -filter "*.jpg" | %{$x=1} {Rename-Item $_.FullName -NewName ('{0}-{1}.jpg' -f ($_.FullName.substring(18,8) -replace("-","")) ,$x++)}
This works well how ever i want to reset the number back to 1 for each separate folder. At the moment i keep numbering up trough different folders. How do i reset $x back to 1 when i change folder?
As you can't be sure that each directory is enumerated at ones, I would create a hashtable to keep track of the index. Something like:
$Directories = #{}
Get-ChildItem -recurse -filter "*.jpg" | ForEach {
Rename-Item $_.FullName -NewName ('{0}-{1}.jpg' -f ($_.FullName.substring(18,8) -replace("-","")) ,++$Directories[$_.DirectoryName])
}

Moving every n files into a separate folder

I have several files with a repetitive naming convention, e.g.
1*Intro*
2*
3*
…
10*intro*
….
I want to move each module into a separate folder. So, I should separate from every *intro* till the next one.
Also, I should note that files are numbered and sorted.
I guess, the easiest way to do this is to:
1. Get a list of intros.
2. Separate their numbers.
3. Start moving files starting from one number till their smaller than the next one.
$i = 1
Ls *intro* | ft {$_.name -replace '\D.*', ''}
// The reason for .* is that the files are `mp4`.
Ls * | ? {$_.name -match '[^firstNumber-SecondNumber-1]'} | move-item -literalpath {$_.fullname} -destination $path + $i++ + '/' +{$_.name}
So the last command should be something like:
Ls *intro* | % { ls * | ? {…} | move-item … }
Or maybe the move-item itself can do the filtering job.
The regular expression doesn't work and I don't have enough Powershell knowledge to write anything better. Can you think of any script to do that? Also how should I permit move-item to create the folder?
I'll be thankful if someone could edit this post with a better title.
This could be done with a simple Switch. The switch will be run against all items in the current folder (items gotten with the Get-ChildItem cmdlet which you use by it's alias 'LS'). It looks to see if the file has the string "Intro" in the file name. If it does, it creates a new folder with that file's name, and stores that folder's info in the $TargetFolder variable (variable created previously to avoid scoping issues). It then moves the file to that folder, and continues to the next file. If the file does not have "Intro" in its file name it simply moves the file to whatever the last assigned $TargetFolder is that was created.
$TargetFolder = ""
Switch(Get-ChildItem .\*){
{$_.BaseName -match "intro"} {$TargetFolder = New-Item ".\$($_.BaseName)" -ItemType Directory; Move-Item $_ -Destination $TargetFolder; Continue}
default {Move-Item $TargetFolder}
}