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
}
Related
I have a script that is designed to do three things:
Convert all .webp files to .jpg from the main "Test" and all its differently-titled subfolders.
For each directory, make a sub directory called "Ch1" within.
Move all the files into the "Ch1" folder
The script currently looks like this, and works fine for single-titled folders with only .webp or .jpg files in each:
cd D:\TestingGrounds\Test
get-childItem -recurse | Where {$_.extension -eq ".webp"} | rename-item -newname {$_.name -replace ".webp",".jpg"}
$dirs = Get-ChildItem -force D:\TestingGrounds\Test
foreach ($dir in $dirs) {mkdir D:\TestingGrounds\Test\$dir\ch_1; move D:\TestingGrounds\Test\$dir\* D:\TestingGrounds\Test\$dir\ch_1}
I now have multi-chapter folders that already have subfolders with .webp and .jpg files inside pre-made chapter folders, such as "Ch1", "Ch1.5", "Ch2", etc.
I cannot figure out a way to add an exception or exclusion to these multi-chapter folders where they are not touched by the mkdir and move portions, only all .webp files to still be renamed to .jpg
I'm not very familiar with Powershell, much less exception commands. I've tried -notcontains, Where-Object, and making another designator, like $multi = 'Ch*' to be ignored. So far, nothing has worked. It will continue to make a "Ch1" in the multi-chapter subfolders except for the original "Ch1", and move their respective files into them... basically what the original script did. Attached are photos of what I'm trying to do.
1 Before 2 Desired Outcome
Here are some of my attempts:
$dirs = Get-ChildItem -force 'D:\TestingGrounds\Test';
$Multi = 'Ch*';
get-childItem -recurse | Where {$_.extension -eq ".webp"} | rename-item -newname {$_.name -replace ".webp",".jpg"};
if ($folder -notcontains 'Ch*') {mkdir D:\TestingGrounds\Test\$folder\Ch1; move D:\TestingGrounds\Test\$folder\* D:\TestingGrounds\Test\$folder\Ch1}
{mkdir D:\TestingGrounds\Test\$folder\Ch1; move D:\TestingGrounds\Test\$folder\* D:\TestingGrounds\Test\$folder\Ch1} | Where {D:\Testing Grounds\Test\$dir\ -notcontains 'Ch*'}
gci "D:\TestingGrounds\Test" | Where-Object {$_.FullName -notlike "Ch*"} | mkdir D:\TestingGrounds\Test\$dir\Ch1; move D:\TestingGrounds\Test\$dir\Ch1\* D:\TestingGrounds\Test\$dir\Ch1; move D:\TestingGrounds\Test\$dir\Ch1 D:\TestingGrounds\Test\$dir
$dirs = Get-ChildItem -force D:\TestingGrounds\Test; $Multi = 'Ch*'; get-childItem -recurse | Where {$_.extension -eq ".webp"} | rename-item -newname {$_.name -replace ".webp",".jpg"}; if ($dir -notcontains $Multi) {mkdir D:\Testing Grounds\Test\$dir\Ch1; move D:\Testing Grounds\Test\$dir\* D:\Testing Grounds\Test\$dir\Ch1}
Edited to reflect clarification in the question:
This will get all of the .webp files in a given directory, move them to a Chapter folder (and create it if needed), or just rename them to .jpg if the file is in the right place.
#Get all WEBP files in the source folder
$sourcefolder = "C:\Test"
$files = Get-ChildItem $sourcefolder -Recurse -Filter *.webp
$chapter = "Ch1"
#Loop through each of the files
Foreach($f in $files)
{
$newname = $f.Name -replace ".webp",".jpg"
$directory = $f.DirectoryName
#Case insensitive RegEx to see if the file is already in a chapter folder, rename the file.
if($f.DirectoryName -imatch ".*\\ch\d+")
{
Rename-Item $f.FullName -NewName $newname
}
#Chapter folder NOT exists, and, File is NOT in chapter folder. Otherwise we'll create sub chapter folders
If((!(Test-Path "$directory\$chapter")) -and($f.DirectoryName -inotmatch ".*\\ch\d+"))
{
New-Item -Path "$directory\$chapter" -ItemType Directory
}
#Chapter folder EXISTS and File is NOT in chapter folder. We can now move it where it needs to be
If((Test-Path "$directory\$chapter") -and ($f.DirectoryName -inotmatch ".*\\ch\d+"))
{
Move-Item $f.FullName -Destination "$directory\$chapter\$newname"
}
}
I have to create a script that searches for file, takes part of the folder name and move the file to a new location with that new name.
I am planning to use powershell for this but would be up willing to look for other options. This used for millions of files.
Example 1
sourcefolder\a\b\test_123456\example.txt -> \destinationfolder\example_123456.txt
Problem is I don't know how many folders deep the file is and the amount of folder name changes, I need everything after the last _
Example 2
sourcefolder\a\b\c\test_test_1234\example.txt -> \destinationfolder\example_1234.txt
I am researching how to script and will update question when I when I have some progress
FileInfo objects include many properties. One of these is the .Directory property which returns the directory (as DirectoryInfo object) that represents the parent folder the file is in. This Directory also has properties, like .Name.
You can use this like below:
$sourceFolder = 'D:\Test' # the root folder to search through
$destinationFolder = 'X:\Archive' # the destinationpath for the moved files
# make sure the destination folder exists
$null = New-Item -Path $destinationFolder -ItemType Directory -Force
# get a collection of FileInfo objects
# if you need more file extensions like both .txt and .log files, replace -Filter '*.txt' with -Include '*.txt', '*.log'
# this will be slower than using -Filter though..
$filesToMove = Get-ChildItem -Path $sourceFolder -File -Filter '*.txt' -Recurse | Where-Object {$_.Directory.Name -like '*_*'}
# using a foreach(..) is a bit faster than 'ForEach-Object'
foreach ($file in $filesToMove) {
# get the last part after '_' of the parent directory name
$suffix = ($file.Directory.Name -split '_')[-1]
# combine to create the new path and filename
$target = Join-Path -Path $destinationFolder -ChildPath ('{0}_{1}{2}' -f $file.BaseName, $suffix, $file.Extension)
$file | Move-Item -Destination $target -Force -WhatIf
}
Take off the WhatIf safety switch if you are satisfied what is displayed on screen about what would be moved is correct.
You don't even need the foreach loop because Move-Item can handle a scriptblock as parameter for the Destination like this:
$sourceFolder = 'D:\Test' # the root folder to search through
$destinationFolder = 'X:\Archive' # the destinationpath for the moved files
# make sure the destination folder exists
$null = New-Item -Path $destinationFolder -ItemType Directory -Force
# get a collection of FileInfo objects
# if you need more file extensions like both .txt and .log files, replace -Filter '*.txt' with -Include '*.txt', '*.log'
# this will be slower than using -Filter though..
$filesToMove = Get-ChildItem -Path $sourceFolder -File -Filter '*.log' -Recurse |
Where-Object {$_.Directory.Name -like '*_*'} |
Move-Item -Destination {
$suffix = ($_.Directory.Name -split '_')[-1]
Join-Path -Path $destinationFolder -ChildPath ('{0}_{1}{2}' -f $_.BaseName, $suffix, $_.Extension)
} -Force
Here, the $_ Automatic variable is used instead of a variable you define in a foreach loop.
P.S. If you only need files from subfolders with a name ending in _ followed by numbers only as opposed to names like sub_folder, change the Where-Object {...} clause in the code to
Where-Object {$_.Directory.Name -match '_\d+$'}
I have a a bunch of language folders present in a directory under E:\Data\ like hu-hu, de-de etc.. on the other hand i have a bunch of file names in G:\ that contain the part of folder name for e.g.
amd64.de-de_OCR.cab,amd64.handwriting.de-de.cab
I need to copy all matching file names based on the foldername
for e.g. de-de should copy all matching files in G:\ i.e. both amd64.de-de_OCR.cab,amd64.handwriting.de-de.cab
This is the code i have so far but it is not copying over the files, and i am not sure how to proceed next, any help is appreciated.
$listfoldername = Get-ChildItem -Path "E:\Data" -Recurse -Directory -Force -ErrorAction SilentlyContinue | Select-Object Name
$destfolder = Get-ChildItem -Path "E:\Data" -Recurse -Directory -Force -ErrorAction SilentlyContinue | Select-Object FullName
$filename = Get-ChildItem -file G:\
if($filename -like $listfoldername)
{
Copy-Item -Path $filename -Destination $destfolder
}
There's a few issues with your code
The main issue with your code is that you are trying to use the -like operator to compare two objects (your object containing the directories you wish to move files to, and the object containing the files.
What you need to do is loop through each file and directory, one by one, to determine if the directory name (e.g. "hu-hu" is found in the filename (e.g. amd64.hu-hu_OCR.cab)
You'll want to use the wildcard indicator "*" with the -like operator (e.g. "*hu-hu*")
This below code snippet should do the trick. I tested using the file and folder names you've provided.
"G:" contains the folders:
de-de
hu-hu
us-us (note, I added this to make sure the code did not match this directory)
"E:\Data" contains the files
amd64.de-de_OCR.cab
amd64.handwriting.de-de.cab
amd64.handwritinghu-hu.cab
amd64.hu-hu_OCR.cab
$FileDirectory = "G:" # Change to "G:\", the trailing slash breaks syntax highlight on SO
$DataDirectory = "E:\Data"
$listfoldername = Get-ChildItem -Path "$DataDirectory" -Recurse -Directory -Force -ErrorAction SilentlyContinue | Select-Object Name
$filename = Get-ChildItem -file "$FileDirectory"
#Loop through each file one at a time
foreach ($file in $filename) {
# Then, loop through each folder one at a time
foreach ($folder in $listfoldername) {
# Set the current filename and listfoldername to variables for later -like operator
$FileString = $file.Name
$FolderString = $folder.Name
# If the current file "is like" the current folder name
if($FileString -like "*$FolderString*")
{
# Set the name of the current folder to a variable
$DataFolder = $folder.Name
Copy-Item -Path "$FileDirectory\$FileString" -Destination "$DataDirectory\$DataFolder"
} else {
Write-Output ("$FolderString pattern not found in $FileString")
}
}
}
I think you should start off by getting a list of possible language target folders. Then loop over the path where the files are, filtering their names to have at least the dash in it and next test if any of the language target folders matches the filename.
Something like this:
$langFolder = 'E:\Data'
$fileFolder = 'G:\' #'# dummy comment to fix syntax highlighting in SO
# get a list of the language folders
# if the languages folder has multiple subdirectories to include, add -Recurse here
$targetFolders = Get-ChildItem -Path $langFolder -Directory
# get a list of FileInfo objects for the files in the G:\ path
# if you need to search subdirectories aswell, add -Recurse here
$files = Get-ChildItem -Path $fileFolder -File -Filter '*-*.*'
foreach($file in $files) {
# check if a language name matches the file name
foreach($folder in $targetFolders) {
if ($file.BaseName -like "*$($folder.Name)*") {
# we have found a matching language target directory
$file | Copy-Item -Destination $folder.FullName
break # exit this folder foreach loop and get on with the next file
}
}
}
P.S. If all the files are .cab files you could speed up by setting the Filter to '*-*.cab' in line $files = Get-ChildItem ...
I have folder Main that has many subfolders (AA,AB,AC,...,ZZ), every subfolder has 5 folders (1,2,3,4,5) in which one them can have .csv file.
I need a to write a script that would copy every .csv file into output folder and rename it based on in which subfolder it was found (AA.csv, BB.csv and so on) all I managed to do was get a list of csv files and create output folder.
New-Item C:\Output -force
$FileExtension = ".csv"
$Dir = get-childitem $FolderPath -recurse
$List = $Dir | where {$_.extension -eq $FileExtension}
$List | format-table Name
I suppose you can have multiple csv files into your dirs, i propose this solution
$Pathsource="C:\Temp\" # let the '\'
$Pathdestination="C:\Temp2\"
#remove -whatif if its ok
Get-ChildItem $Pathsource -Recurse -Filter "*.csv" | %{Copy-Item $_.FullName ($Pathdestination + $_.FullName.Replace($Pathsource , '').Replace('\', '_')) -WhatIf}
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++
}