I am trying to rename files in subfolders in a certain pattern, but I am stuck.
The situation is as follows: I have multiple folders which are sometimes named as the target filename depending on the length, but the name does not really matter.
In each folder are always 2 files: the Target-File with a random name and the correct extension, and the Source-File which is always the correct BaseName with a txt-extension.
For example:
Folder1\7393028473.docx
Folder1\January.txt
Folder2\9373930843.pdf
Folder2\February.txt
My goal is to rename every not-txt-file with the Basename of the txt-file. Executed, it should be like:
Folder1\January.docx
Folder1\January.txt
Folder2\February.pdf
Folder2\February.txt
With gci I was able to create both lists but didn't find a good way for the renaming.
$SourceName = gci -File -Recurse | Where {$_.Extension -ne ".txt"}
$TargetName = gci -File -Recurse | Where {$_.Extension -eq ".txt"}
I did also try to use gci for renaming, but was not able to tell it to use the newname based on the txt-file:
gci -File -Recurse | Where {$_.Extension -ne ".txt"} | Rename-Item -NewName {$_.extension -eq ".txt"}
This only renamed the .docx-file to "FALSE" because the filename already exists.
What I did not try (but would be ok) is to not only rename the file, but also move it to the parent directory.
This is one way to do it but it would fail as soon as there are 2 or more files with a different extension than .txt but having the same extension. It would also fail as soon as one folder has more than one .txt file.
# Get all folders under 'TargetDirectory'
Get-ChildItem TargetDirectory -Directory -Recurse | ForEach-Object {
# For each sub-folder, get their files
$childs = $_.EnumerateFiles()
# Filter and split the child files by their extension
$txt, $notTxt = $childs.Where({ $_.Extension -eq '.txt' }, 'Split')
# Use the BaseName of the '.txt' File but the Extension of
# the file being renamed
$notTxt | Rename-Item -NewName { $txt.BaseName + $_.Extension }
}
Thanks for your reply and sorry for my late reply.
I tried your code but its not working correctly:
The NewName is created correctly, but the problem is the rename-function or rather the notTxt list because it only contains the item itself but not hte full path.
When I copy the file which should be renamed into the parent-directory your code does work in the file in the parent-directory.
There was another answer which apperntly was deleted but did work.
I also tried a foreach-loop in one of my tries but didn't get the NewName to work.
I don't know why, but I didn't consider creating the NewName with a variable, which was done in the deleted answer:
$folders = gci -Directory -Recurse
foreach ($folder in $folders) {
$targetFile = gci $folder | Where {$_.Extension -ne ".txt"}
$sourceFile = gci $folder | Where {$_.Extension -eq ".txt"}
$newName = $sourceFile.BaseName + $targetFile.Extension
Rename-Item $targetFile.FullName $newName
}
Of course you can try and get your code to work, but I can make do with this code.
Thank you very much for your help.
Related
I have a file directory that contains many folders within it. Inside each of these sub-folders, I have a variety of files. I would like to go through each file, rename some of the items, and add extensions to some of them. I am using Powershell to do this.
I have file names with "." that all need to be replaced with "_" for example, "wrfprs_d02.03" should be "wrfprs_d02_03". I was able to successfully do that in one folder with the following code:
dir | rename-item -NewName {$_.name -replace "wrfprs_d02.","wrfprs_d02_"}
After, I make those replacements, I want to add .grb extensions on to some of the files, which all happen to start with "w", and I was able to do that within one folder with:
Get-ChildItem | Where-Object {$_.Name -match "^[w]"} | ren -new {$_.name + ".grb"}
When I step back from one folder and try to do it iteratively within many folders, my code doesn't work. I am in a directory called "Z:\Windows.Documents\My Documents\Test_data\extracted" which contains all my sub-folders that I want to iterate over. I am using the following code:
$fileDirectory = "Z:\Windows.Documents\My Documents\Test_data\extracted"
foreach($file in Get-ChildItem $fileDirectory)
{
dir | rename-item -NewName {$_.name -replace "wrfprs_d02.","wrfprs_d02_"}
Get-ChildItem | Where-Object {$_.Name -match "^[w]"} | ren -new {$_.name + ".grb"}
}
Any ideas on what my problem is?
because you $_ is replaced into loop when you use pipe. I propose you a new code:
$fileDirectory = "Z:\Windows.Documents\My Documents\Test_data\extracted"
Get-ChildItem $fileDirectory -recurse -file -filter "*.*" |
%{
#replace . by _
$NewName=$_.Name.Replace(".", "_")
#add extension grb if name start by w
if ($NewName -like "w*") {$NewName="$NewName.grb"}
#Add path file
$NewName=Join-Path -Path $_.directory -ChildPath $NewName
#$NewName
#rename
Rename-Item $_.FullName $NewName
}
Not sure what error you were getting, but using rename-item can be finicky. Or at least so in my experience.
I used the follow without issue. My files names were different so I replaced all periods with underscores. If the file starts with "W" then it changed the extension for that file.
$FilePath = Get-ChildItem "Z:\Windows.Documents\My Documents\Test_data\extracted" -Recurse -File
foreach ($file in $FilePath)
{
$newName = $file.Basename.replace(".","_")
$New = $newName + $file.Extension
if($file.Name -match "^[w]")
{
Rename-Item $file.FullName -NewName "$($New).grb"
}
else
{
Rename-Item $file.FullName -NewName $New
}
}
Hope that helps.
Im still generally new to powershell, and I am trying to create a program that will take files based on their name, and move them into folders that have a similar name but not exactly the same.
For example, Lets say I have 3 files, Apples.txt, Grapes.txt, and Oranges.txt. And I want to move them into corresponding folders, ApplesUSA, GrapesNY, OrangesFL.
I could just hard code it using a loop and a If-Then Statement. i.e If Apples.txt exists move to ApplesUSA. But I want it to be dynamic, so if other files and folders are added later I dont have to update the code. Is there a way to write a statement that would say if FileA and FolderB are similar in name (both contain apples in the name somewhere) then move fileA to FolderB and so on.
Any help appreciated. Thanks!!!!
try Something like this
$PathWithFile="C:\temp\Test"
$PathWithDir="C:\temp\Test"
Get-ChildItem $PathWithFile -file -Filter "*.txt" | %{
$CurrentFile=$_
$Dirfounded=Get-ChildItem $PathWithDir -Directory | where {$_ -match $CurrentFile.BaseName} | select FullName -First 1
if ($Dirfounded -ne $null)
{
move-Item $CurrentFile.FullName -Destination $Dirfounded.FullName -WhatIf
}
}
A oneliner similar to #Esperento's
gci *.txt -af|%{$File=$_.FullName;gci "$($_.BaseName)*" -ad|%{Move $File -Dest $($_.FullName) -whatif}}
The verbose version:
PushD "X:\path\to\base\folder"
Get-ChildItem *.txt -File | ForEach-Object{
$File = $_.FullName
Get-ChildItem "$($_.BaseName)*" -Directory | ForEach-Object {
Move-Item $File -Destination $_.FullName -whatif
}
}
PopD
Both versions require PowerShell V3 for the -File and -Directory parameters (and their aliases -af/-ad) This can be substituted by an additional |Where-Object{ $_.PSIsContainer} respective | Where-Object{!$_.PSIsContainer}
I am really new into powershell, I googled alot and merged my results into this, but to begin with, my folder structure is like this:
test-neu
_bewertet
1.txt
2.txt
1
1.txt
2.txt
2
1.txt
3.txt
4.txt
...
My goal is to look for all the files stored in _bewertet and if these files also exist in any other subfolder than itself (regarding, filename and size), it should be deleted in there. So to be clear, the files should stay in _bewertet but nowhere else.
$ignore = #("*_bewertet*");
$one = Get-ChildItem -Recurse -path C:\Users\name\Desktop\test-neu\_bewertet
$two = Get-ChildItem -Recurse -path C:\Users\name\Desktop\test-neu -Exclude $ignore | ? { $_.PSIsContainer }
$matches = (Compare-Object -ReferenceObject $one -DifferenceObject $two -Property Name,Length -ExcludeDifferent -IncludeEqual)
foreach ($file in $matches)
{
Remove-Item C:\Users\name\Desktop\test-neu\$($file.Name)
}
So far, it really does nothing if I run my script..., my guess is, the Remove-Item part needs to be adjusted. The path just goes to C:\Users\name\Desktop\test-neu\1.txt for example and so the appropriate subfolder is missing.
If you guys could help me out there, that would mean alot to me. Any suggestions are welcome. Have a nice day. :)
This is the simplest logic I could come up with, to achieve this goal
$keep = Dir -Path t:\bewertet -Recurse
$allFiles = Dir -Path t:\ -Recurse -Include *.txt | Where Directory -NotLike "t:\bewertet*"
$allFiles | Where Name -in $keep.Name | Remove-Item
$Keep defines the list of files in the bewertet folder.
$allFiles defines a list of all files recursively, specifying only .txt files, and where the directory is not t:\bewertet.
Finally, we filter through $allFiles, finding files with matching name to the files in $keep and then remove each of those.
Even more better
OP came back and said We forgot to check if it's the same size!, so being that I'm in the Christmas Spirit, I wrote this lazy comparison.
$keep = gci t:\bewertet -Recurse
$allFiles = dir -Path t:\ -Recurse -Include *.txt | Where Directory -NotLike "t:\bewertet*"
$matches = $allFiles | ? Name -in $keep.Name
ForEach ($match in $matches){
$source = $keep | ? Name -eq $match.Name
$Samefile = ($source.Length -eq $match.length)
"Does file size match? [$Samefile]"
if ($Samefile){
"deleting..."
Remove-item $match
}
}
I have 10K documents in a directory with this type of naming convention:
1050_14447_Letter Extension.pdf, 1333_14444_Letter.docx, etc...
I tried using this script to remove all characters before the 2nd underscore (including the 2nd underscore):
Get-ChildItem -Filter *.pdf | Rename-Item -NewName {$_.Name -replace '^[0-9_]+'}
This worked, but revealed there would be duplicate file names.
Wondering if there is some way a script can create a subfolder based on the filename (minus the extension)? So there would be 10K subfolders in my main folder. Each subfolder would just have the one file.
This should work. It creates a new folder for each item, then moves it, renaming it in the process.
gci | ? {!$_.PSIsContainer} | % {New-Item ".\$($_.BaseName)" -Type Directory; Move-Item $_ ".\$($_.BaseName)\$($_.Name -replace '^[0-9_]+')"}
Note that if there are two files with the same name, but different extensions, you'll see an error when trying to create the directory, but both files will wind up in the same folder.
Alternately, if you want something more readable to save in a script, this is functionally identical:
$files = Get-ChildItem | Where-Object {!$_.PSIsContainer}
foreach ($file in $files)
{
$pathName = ".\" + $file.BaseName
New-Item $pathName -Type Directory
$newFileName = $pathName + "\" + ($file.Name -replace '^[0-9_]+')
Move-Item $file $newFileName
}
Is it possible to define an array of filenames (all files in different folders) and then in a loop delete them all, or do something else?
Actually I need to create a few symbolic links using mklink to one file, putting those links in a different folders, replacing the old links if there was any.
Deleting an array of filenames is simple:
Remove-Item foo.txt,c:\temp\bar.txt,baz\baz.txt
Or via a variable:
$files = 'foo.txt','c:\temp\bar.txt','baz\baz.txt'
Remove-Item $files
And then based on all files in different folders:
$folders = 'C:\temp','C:\users\joe\foo'
Get-ChildItem $folders -r | Where {!$_.PSIsContainer} | Remove-Item -WhatIf
Remove the -WhatIf to do the actual removal.
If you want to delete a file with a specific name you could use the -Filter parameter on Get-ChildItem. This would be the best performing approach:
$folders = 'C:\temp','C:\users\joe\foo'
Get-ChildItem $folders -r -filter foo.bak | Remove-Item -WhatIf
If the name requires more sophisticated matching then you can use a regex in a Where scriptblock e.g.:
$folders = 'C:\temp','C:\users\joe\foo'
Get-ChildItem $folders -r | Where {$_.Name -match 'f\d+.bak$'} |
Remove-Item -WhatIf
something like this should work:
can't test right now, sorry
$filenames = #('filename1.txt', 'filename2.txt', 'filename3.txt')
foreach($file in $filenames)
{
#GCI recursive to find all instances of this filename
$filesToDelete = Get-ChildItem -R | where {$_.Name -eq $file}
foreach($f in $filesToDelete)
{
#delete the file that matches, etc. here
# just using Write-Host to echo out the name for now
Write-Host $f.Name
}
}
As with most powershell, you can really compress this, but wanted to extend for explanation.
You could extend this to match your needs. For example if you needed all files that contain the word "delete", you could do gci | where {$_.Name -like "$file"}