Rename or Remove Characters From a Filename with PowerShell - windows

I am trying to rename or remove part of a filename. I have a scheduled job that moves an updated file from one server to another. Once the file is moved it need to be renamed. For example: Filename_01-23-AB.exe to Filename.exe I want to remove everything from the "_" to the "." and leave the extension intact. I found the following code that should do this but I can't seem to get it to work. Am I headed down the right path here?
## Removes the build number from the filename of "Filename_XX-XX-XX.exe" leaving the new filename to be "Filename.exe" ##
$File = -Path "C:\Temp\TestPath\"
foreach ($File in gci *.exe) {
$Fname = ($File.name).split('.')[0] ## item before the '.' ##
$Prefix = $Fname.split('_')[0] ## item before the '_' ##
$Newname = $Prefix + '.exe'
Rename-Item $file $Newname
}

I've tried simplifying what you're tying to do:
pushd "C:\Temp\TestPath\"
dir | ? { $_.Name -like "*.exe" } |
% { Rename-Item $_ "$($_.BaseName.split('_')[0])$($_.Extension)" }
popd
First, this changes the directory your PowerShell is looking at to your defined directory (pushd).
Then it will get the contents of your directory, find all objects with the extension of .exe, and split the file's base name at the underscore. popd will then set the directory back to where-ever it was previous to running this script.

I think this can be simplifyed even more using a regex to remove everything between the first underscore and the file extension:
$Path= 'C:\Temp\TestPath\'
Get-ChildItem $Path -Filter "*_*.exe" | foreach {
$_ | Rename-Item -NewName ($_.Name -replace '(?=_).+(?=\.)')
}
This also works when the filename doesn't contain an underscore.
And here the regex demo.

!not tested
try this
$path = 'C:\Temp\TestPath\'
gci $path *.exe | ? basename -match '_' | % {
# if the file.txt already exists, rename it to file-1.txt and so on
$num = 1
$base = $_.basename.substring(0, $_.basename.indexof('_'))
$ext = $_.extension
$dir = $_.directory
$newname = Join-Path $dir "$base$ext"
while (Test-Path $newname) {
$newname = Join-Path $dir "$base-$num$ext"
$num++
}
# finally, rename the file
ren $_.fullname $newname
}

Related

Is there a way to rename files in bulk by the name of their folder?

I'm trying to write a PowerShell script that goes into every folder in a certain directory, and every child with the parent.
Example:
Z:\Folder1\File.txt
Z:\Folder1\Picture.jpeg
Z:\Folder1\Data.csv
Z:\Folder2\File.txt
Z:\Folder2\Picture.jpeg
Z:\Folder2\Data.csv
=
Z:\Folder1\Folder1.txt
Z:\Folder1\Folder1.jpeg
Z:\Folder1\Folder1.csv
Z:\Folder2\Folder2.txt
Z:\Folder2\Folder2.jpeg
Z:\Folder2\Folder2.csv
I have this so far
dir | rename-item -NewName {$_.name -replace *,"Folder1"}
Please let me know how I can loop through every folder, and how I can rename them based on the folder name.
Thanks!
This should do what you're asking:
Get-ChildItem Z:\Folder* -Directory | Get-ChildItem -File | Rename-Item -NewName { $_.DirectoryName.split('\')[-1] + $_.Extension } -WhatIf
Adjust the folder name filter (.\Folder*) as required and remove the -WhatIf if you're satisfied with the changes it will make.
Obviously this doesn't account for where you might have a name clash if there are two files in the folder with the same extension. The following would do that:
$FilesToRename = Get-ChildItem Z:\Folder* -Directory | Get-ChildItem -File
foreach ($FileToRename in $FilesToRename) {
$NewName = $FileToRename.DirectoryName.split('\')[-1] + $FileToRename.Extension
$NewFile = Join-Path $FileToRename.Directory $NewName
$i = 0
While (Test-Path $NewFile) {
$i = $i++
$NewName = $FileToRename.DirectoryName.split('\')[-1] + "[$i]" + $FileToRename.Extension
$NewFile = Join-Path $FileToRename.Directory $NewName
}
$FileToRename | Rename-Item -NewName $NewName
}

Rename all folders in Directory to single digit or character with Powershell

I have a directory in Windows 10 where all the files have names that are too long for windows to handle.
I want to delete these files.
I have discovered that renaming the folder allows me to reduce the path name enough to delete the folder.
For example Changing "Desktop" and it's subfolders to "1" (so the path is 1/1/1/1/file.filetype)
What I have tried is:
Get-ChildItem $path -Recurse -Filter * | Rename-Item -NewName { $_.name -replace *, '1'} -verbose
However this seems to throw an error on *
Is there an easy way to do this or what can I change?
Edit:
Dir | %{Rename-Item $_ -NewName ("{0}" -f $nr++)}
Works on one level but I am having trouble making that recursive through the child folders
Get-ChildItem $path -Recurse | %{Rename-Item $_ -NewName ("{0}" -f $nr++)}
throws this error among others:
Rename-Item : Cannot rename because item at 'designable.nib' does not exist.
If you know you will not loose anything by renaming all folders, this would help. This does not change filenames. This will recursively call the method to rename the folder until the process is able to find the next number.
NOTE: If you take away -Directory, it will update the filenames as well.
function RenameToLeastNumbers ($item, $number) {
try {
$newName = $item.Name -replace $item.Name, $number
Rename-Item $item.FullName -NewName $newName -ErrorAction Stop
}
catch
{
$number = $number + 1
if ($number -gt 100) {
return
}
RenameToLeastNumbers $item $number
}
}
Get-ChildItem -Directory -Recurse -Path C:\Temp\Dates | % { RenameToLeastNumbers $_ 1 }
All the folders under Dates will be renamed starting with 1. If Dates have three folders, they will be renamed to 1, 2, 3. Same thing will happen at each of their childrens and so on.
what folders look like
dir C:\temp\Dates -Name -Recurse
1
2
3
1\1
1\1\This is a text document.txt
2\1
3\1
I tried this process and did not once see the error for many many directories nested in 100s of folders. (created a test folder with nothing but directories and kept copying them within each).
function RenameFolderAndSubFolders {
param($item, $number)
$subfolders = Get-ChildItem $item.FullName -Directory
foreach ($folder in $subfolders) {
RenameFolderAndSubFolders $folder 1
}
while ($true){
try {
Write-Output "Renaming: $($item.FullName)"
Rename-Item $item.FullName -NewName $number -ErrorAction Stop
return
}
catch {}
$number = $number + 1
}
}
Get-ChildItem -Directory -Path C:\Temp\Dates | % { RenameFolderAndSubFolders -item $_ -number 1 }
Let me know how this works.

How to add leading zeros to a batch of file names before a key character using powershell

I'm trying to add leading zeros to a batch of file names before an underscore.
e.g.: going from 123_ABC.pdf to 000123_ABC.pdf
The goal is that before the underscore there should be 6 numbers, and I therefore need to add leading zeros.
I have done this before for cases where i needed to add leading zeros to a file name that was pure numbers, which is the below code, but I'm not sure how to adapt it to the scenario above.
Get-ChildItem "[Folder Location]" | ForEach-Object {
$NewName = "{0:d6}$($_.Extension)" -f [int]$_.BaseName
Rename-Item $_.FullName $NewName
}
Any help would be really appreciated.
Thanks
Here's how you can get the new file name according to your specifications:
$input = "123_ABC.pdf","_ABC.pdf", "qksdcfg.pdf", "0140ABC.pdf", "014_0_ABC.pdf"
foreach($filename in $input) {
# split on first underscore
$parts = $filename -split "_",2
# if there are more than 1 parts (= there is an underscore in the filename)
if($parts.Count -gt 1) {
# add leading 0's and join with the file name remainder
"{0:d6}_{1}" -f [int]$parts[0], $parts[1]
} else {
$filename
}
}
Output is:
000123_ABC.pdf
000000_ABC.pdf
qksdcfg.pdf
0140ABC.pdf
000014_0_ABC.pdf
Mixed with your code:
Get-ChildItem "[Folder Location]" | ForEach-Object {
$parts = $_.Name -split "_",2
if($parts.Count -gt 1) {
$NewName = "{0:d6}_{1}" -f [int]$parts[0], $parts[1]
} else {
$NewName = $_.Name
}
Rename-Item $_.FullName $NewName
}
try Something like this
Get-ChildItem "c:\temp\*.pdf" -file -filter "*_*" | %{
$Array=$_.Name.Split('_')
$NewName="{0:d6}_{1}" -f [int]$Array[0], ($Array[1..($Array.Length -1)] -join '_')
Rename-Item $_.FullName -NewName $NewName
}

Delete All But The Newest File of Pattern In Each Subdirectory

Requirements:
Iterate through all folders and files in each directory
Delete all files in each subdirectory except for the newest one (contains filename time stamp) and has a specific filename pattern or instead copy the one file in each subdirectory that is the newest and that is preceded with LIVE_DATA. (There are other files in each subdirectory I wish to ignore).
Directory: AISI-301.0000000001006165.2015-08-24_23-57-46-1
File: LIVE_DATA_2015-08-24_23-57-51.413+0000.MCA
How can I iterate through all of the files recursively while doing comparisons between files in each subdirectory only? I don't want to compare all of them together. Just the one's inside each subdirectory.
for $filename in Get-ChildItem -recurse {
for $filename in $directory {
if ($filename > $filename2) {
$temp = $filename
}
$filename2 = $filename1
}
}
This is what I ended up using to rename my file's after the directory names. It appends the file name to the end and the directory name comes first. I hope this helps somebody else that might be searching for this solution.
Get-ChildItem -include *LIVE_DATA_*.MCA -recurse | Rename-Item -NewName { $_.Directory.Name+'_'+$_.Name}
If you want to remove all files LIVE_DATA_* except the most recent one on a per-folder basis you could do something like this:
$root = 'C:\path\to\root\folder'
Get-ChildItem $root -Recurse | ? { $_.PSIsContainer } | ForEach-Object {
Get-ChildItem (Join-Path $_.FullName 'LIVE_DATA_*') |
Sort-Object Name -Desc |
Select-Object -Skip 1 |
Remove-Item -Force
}
Get-ChildItem $root -Recurse | ? { $_.PSIsContainer } lists all subfolders of $root. Then the ForEach-Object runs another Get-ChildItem statement (without recursion) for each subfolder separately. The Join-Path statement builds a wildcard path from the filename pattern and the full path to the folder (C:\path\to\root\folder\sub\folder\LIVE_DATA_*).
Basically the code lists all folders, then processes the files for each individual folder.

How to list files with a for loop?

How I can do a ls using PowerShell?
for i in `ls`
do
if [ -d $i ] #miro si és directori
then
echo "Directory"
else echo "File"
fi
done
POWERSHELL
$llistat -ls
forEach $element in $llistat ??? this is possible
}
A more PoSh way is to use a pipeline, and perhaps a hashtable:
$type = #{
$true = 'Directory'
$false = 'File'
}
Get-ChildItem | ForEach-Object { $type[$_.PSIsContainer] }
PowerShell even has a default alias ls for Get-ChildItem, so you could use more Unix-ish syntax:
ls | % { $type[$_.PSIsContainer] }
In PowerShell, the Get-ChildItem cmdlet works like ls (at least with the file system provider). All items returned have a PowerShell-specific property called PSIsContainer, indicating whether it's a directory or not:
foreach($item in (Get-ChildItem)){
if($item.PSIsContainer){
"Directory"
} else {
"File"
}
}
If you want to see what's inside each directory, one level down:
foreach($item in (Get-ChildItem)){
if($item.PSIsContainer){
# Directory! Let's see what's inside:
Get-ChildItem -Path $item.FullName
}
}
As of PowerShell version 3.0 and up, the Get-ChildItem supports a File and Directory switch on the filesystem provider, so if you ONLY want directories, you could do:
Get-ChildItem -Directory
So the second example becomes:
Get-ChildItem -Directory | Get-ChildItem
You could also list files recursively (like ls -R):
Get-ChildItem -Recurse

Resources