I have an exported CSV file with the below table
"Directory","BaseName"
"E:\Movie Files\Movie (2000)","Movie"
"E:\Movie Files\Movie (2001)","Movie 2"
My code is very close to what I want and that is basically rename the cover.jpg in each folder to the basename of the movie and add -poster.jpg after it, e.g. Movie-poster.jpg, Movie 2-poster.jpg.
It works for the first folder but every folder after this uses the same basename from the first folder. It doesn't recurse for every folder within the root location like I thought it would. So for Movie (2001) I end up having Movie-poster.jpg, not Movie 2-poster.jpg.
This is the code that's not quite there:
$lines = Import-Csv 'E:\Output.csv'
foreach ($line in $lines) {
Get-ChildItem -Path .\* -File -Filter '*Cover*' -Recurse |
Rename-Item -NewName ($line.BaseName + '-poster.jpg')
}
Expected results as mentioned above is that each cover.jpg in each folder and sub-folders will be renamed to match the basename according to their locations.
You end up with all covers being renamed to Movie-poster.jpg because your code renames all covers under the current working directory (.) in the first iteration of your loop, so there are no files with the string "Cover" in their name left when the loop goes into the second iteration.
Process the directories from your CSV instead of processing the current working directory for each of them, and use the current object from the pipeline ($_) for renaming the files. For the latter you also must replace the parentheses with curly brackets.
Change this:
foreach ($line in $lines) {
Get-ChildItem -Path .\* -File -Filter '*Cover*' -Recurse |
Rename-Item -NewName ($line.BaseName + '-poster.jpg')
}
into this:
foreach ($line in $lines) {
Get-ChildItem -Path $line.Directory -File -Filter '*Cover*' -Recurse |
Rename-Item -NewName {$_.BaseName + '-poster.jpg'}
}
Thank you Ansgar for the comment. It didn't work in my instance, what it did was rename ALL the files (including those that already had the correct naming convention) to be cover-poster.jpg however the code did point me in the right direction with regards to $line.Directory.
The code that worked for me in the end was this;
# Renames Cover.jpg to match BaseName file with basename-poster.jpg
$lines = Import-Csv 'E:\Output.csv'
ForEach ($line in $lines) {
Get-ChildItem -Path $line.Directory -file -filter '*Cover*' -Recurse | Rename-Item -NewName {$line.BaseName + '-poster.jpg'}
}
Related
I am trying to use PowerShell to read filenames from a dir;
then within a for loop:
split names using a delimiter; store desired output in a new variable. Now I want to replace the original filenames in the directory with this new variable. So far I have gathered the following with the expected outputs shown:
$files = Get-ChildItem -Path C:\Test
write-output $files
Directory: C:\Test
1_N04532L_LEFT.JPG
2_N04532R_RIGHT.JPG
code continues
foreach ($file in $files)
{
$nameArray = $file -split "_"
$newName = $nameArray[1]
write-output $newName
}
N04532L
N04532R
Any Ideas on how to accomplish this. I am not a programmer and there is lots of data on this, but it's not working for me.
As both commenters already explained, there is the Rename-Item cmdlet for renaming files.
Since this cmdlet can take a scriptblock in its NewName parameter, you can use that to create a new filename.
# adding switch -File makes sure you do not also try to rename subfolders
$files = Get-ChildItem -Path 'C:\Test' -File
foreach ($file in $files) {
$file | Rename-Item -NewName { '{0}{1}' -f ($file.BaseName -split '_')[1], $file.Extension }
}
You can shorten this by piping the results from Get-ChildItem trhough one-by-one to the Rename-Item cmdlet.
Because we're piping the FileInfo objects here, we can make use of the $_ automatic variable
# enclose the Get-ChildItem cmd in brackets so this will enumerate the files to completion
# before passing them on to te Rename-Item cmdlet.
# if you don't, files you already have renamed could be picked up and processed again..
(Get-ChildItem -Path 'C:\Test' -File) |
Rename-Item -NewName { '{0}{1}' -f ($_.BaseName -split '_')[1], $_.Extension }
Note: when renaming files, you can always run into naming collisions, upon which you will receive an exception
I have a bunch of directories with files in them and my goal is to loop through a directory structure and write each of the files one by one to a new location.
The files on disk look like this:
- DATAFILE1_DATE_20210101_RUNDATE_20210101.csv
- DATAFILE1_DATE_20210102_RUNDATE_20210102.csv
- DATEFILE2_DATE_20210103_RUNDATE_20210103.json
- DATEFILE2_DATE_20210104_RUNDATE_20210104.json
I'm trying to pass the contents of the directory to a variable $fileSystemItems and then to remove everything after _DATE so that I could build a new directory structure in the target as:
- /target/DATAFILE1/DATAFILE1_DATE_20210101_RUNDATE_20210101.csv
- /target/DATAFILE1/DATAFILE1_DATE_20210102_RUNDATE_20210102.csv
- /target/DATAFILE2/DATEFILE2_DATE_20210103_RUNDATE_20210103.json
- /target/DATAFILE2/DATEFILE2_DATE_20210104_RUNDATE_20210104.json
The PS code I have so far takes the files from a specified directory and outputs them:
$sourcePath = "\\files\data"
$fileSystemItems = Get-ChildItem $sourcePath -Recurse | Where { ! $_.PSIsContainer }
foreach ($file in $fileSystemItems) {
Write-Host "Writing name of the file is $($file.BaseName)"
}
I have tried using the Rename-Item and regex but renaming the files the source is not an option as other programs are accessing the same data, for example:
Get-ChildItem $sourcePath -Recurse | Rename-Item -NewName { $_.Name -replace "^_DATE+","" }
How do I modify the filename in the $file variable in the foreach loop to output both a edited version of the file name (e.g. DATEFILE1) and also the full file name DATAFILE1_DATE_20210101_RUNDATE_20210101.csv ?
Rename-Item is a great tool to begin with! First, let's optimize your code snippet a little bit:
Instead of piping Get-ChildItems output to Where { ! $_.PSIsContainer }, we'll use the -File switch.
Let's also change your regex from ^_DATE+ to _DATE.* and use a pipeline with ForEach-Object:
Get-ChildItem $sourcePath -Recurse -File | ForEach-Object {
Rename-Item -Path $_.Fullname -NewName (($_.Name -replace "_DATE.*","") + $_.Extension)
}
Looking at your destination structure it looks like you might also want to move files to a target directory.
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.
I have the following PowerShell script that puts all .jpg files found in its subfolders into a 'Scans' folder and renames these .jpg files to match the name of their respective parent directory (=subfolder). When multiple .jpg files exist the renaming process will automatically add a number to the filename, like File_1,jpg and File_2.jpg.
So before the script is run a folder may look like this:
Parent Directory from which script is executed
|
Subfolder (containing .jpg files and the 'Scans' subfolder)
|
Scans (=folder)
Photo.jpg
Picture.jpg
Shot(1).jpg
Shot(2).jpg
Once the script has run the folder looks like this:
Parent Directory from which script is executed
|
Subfolder (containing the 'Scans' subfolder)
|
Scans (=folder containing supposed to contain all .jpg files)
|
Subfolder_1.jpg
Subfolder_2.jpg
Shot(1) (=file moved but extension stripped)
Shot(2) (=file not moved and extension stripped)
So the .jpg files containing parenthesis 'Shot(1).jpg' and 'Shot(2).jpg' are not properly renamed and moved to the 'Scans' folder. In fact their '.jpg' extension is stripped off.
The script works fine as long as the .jpg files do not contain any parentheses (()), as in
'Shot(1).jpg' and 'Shot(2).jpg' → when multiple .jpg files exist
'Pics(2001).jpg' → when the subfolder name contains parentheses as in 'Pics(2001)', the .jpg is correctly renamed, but then contains parentheses and the script would fail again when run a second time.
I have read about escaping special characters in other threads but have not been able to implement a solution into the script below. Does anybody here have a solution so that the parentheses are not causing any issues when moving and renaming these .jpg files?
$path = Split-Path -parent $MyInvocation.MyCommand.Definition
function renamePhotos {
# Loop through all directories
$dirs = dir $path -Recurse | Where { $_.psIsContainer -eq $true }
foreach ($dir In $dirs) {
$i = 1
$newdir = $dir.parent.name + "_"
$images = Get-ChildItem -Path $dir.fullname -Filter *.jpg -Recurse
foreach ($image In $images) {
$split = $image.name.split(".jpg")
$replace = $split[0] -Replace $split[0],($newdir + $i + ".jpg")
$image_string = $image.fullname.ToString().Trim()
Rename-Item "$image_string" "$replace"
$i++
Move-Item -Path $dir\*.jpg -Destination $dir\Scans
}
}
}
# RUN SCRIPT
renamePhotos
"SCRIPT FINISHED"
The -replace operator does a regular expression replacement. If you want to use it you need to escape special characters in your source string:
-replace [regex]::Escape($split[0]), "$newdir$i.jpg"
However, you don't need that in the first place. Simply use the appropriate properties of the FileInfo objects:
$images = Get-ChildItem -Path $dir.fullname -Filter *.jpg -Recurse
foreach ($image in $images) {
$newname = $newdir + $i + $image.Extension
Rename-Item $image.FullName $newname
$i++
}
Move-Item -Path $dir\*.jpg -Destination $dir\Scans
Or simply move the items to the new location with their new name without renaming them first:
$images = Get-ChildItem -Path $dir.fullname -Filter *.jpg -Recurse
foreach ($image in $images) {
$newname = $newdir + $i + $image.Extension
Move-Item $image.FullName "$dir\Scans\$newname"
$i++
}
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
}