Move files, check for duplicates and rename incrementally if present - powershell

I have a script that looks at the source folder and then the destination folder and decides if the sub-folders are present. If not, it creates the corresponding folder. Once that is done, it moves all of the files in the sub folders to the destination. Problem - There are duplicate files that need can't be overwritten. I need a way to check for duplicates and if found, increment the source file name by -1. There may be incremented files already.
My current script works flawlessly to create needed sub-folders and move files but I loose any previous files.
$SFolder = "C:\Temp\Test\Source\"
$DFolder = "C:\Temp\Test\Destination\"
$folders = Get-ChildItem -Path $SFolder -Directory
foreach ($folder in $folders) {
Get-ChildItem -File "$sfolder\$folder" |
Where-Object -FilterScript {$_.LastWriteTime -lt [datetime]::Now.AddMinutes(-5)} |
Sort-Object -Property LastWriteTime |
Group-Object -Property {$_.LastWriteTime.ToString("yyyy")} |
ForEach-Object {
if (!(Test-Path -Path "$dfolder\$folder" -PathType Container -ErrorAction SilentlyContinue)) {
New-Item -ItemType Directory -Force -Path "$dfolder\$folder"
}
$_.group|move-item -Destination "$dfolder\$folder" -PassThru
}
}
The script as written works great. Here is what I need. I start with the following:
Source test1\05013018-22503-652469-1 (no file extension)
test1\05013018-22503-652469-2
Destination test2\05013018-22503-652469-1
test2\05013018-22503-652469-1-1
test2\05013018-22503-652469-1-2
I want the following result after running the script:
Source test1\
Destination test2\05013018-22503-652469-1
test2\05013018-22503-652469-1-1
test2\05013018-22503-652469-1-2
test2\05013018-22503-652469-1-3
test1\05013018-22503-652469-2

Related

Powershell: Moving named files to their corresponding folder

enter image description hereI have a folder which has a bunch of files named: WBF123456, WBF135464, etc. These files need to be moved to the corresponding folder. At the moment I am using the commandline to manually enter the numbers of each file so they get moved, using this code:
$files = $args[0]
mv O:\SCAN\SecSur\*$files.pdf O:\SPG\G*\*\*$files
How can I automate this process?
It needs to identify the number in the filename, then move it to the folder containing the same number.
Any help would be great. Thanks.
I need to get the files on the left, inside the corresponding folders on the right.
Maybe the below solution will help you. You should change $origin_path and $destination_path
$origin_path= "C:\Users\geralexgr\Desktop\kati\files"
$destination_path = "C:\Users\geralexgr\Desktop\kati\folders"
Get-ChildItem $origin_path -Recurse -Include *.txt | ForEach-Object {
$folder = [regex]::Matches($_.Name, "\d+(?!.*\d+)").value
Move-Item $_.FullName $destination_path\$folder
}
The example will place files under the folders that match the numeric regex.
After powershell execution file WBF12 gets inside 12 folder
Apparently the files to move are .pdf files, so what you can do is get a list of those files in the source folder and then loop over that list to create (if needed) the destination subfolder and move the file there.
Try:
$destinationRoot = 'O:\SPG\G\SomeWhere' # enter the root folder destination path here
$filesToMove = Get-ChildItem -Path 'O:\SCAN\SecSur' -Filter '*.pdf' -File
foreach ($file in $filesToMove) {
$numName = $file.BaseName -replace '\D+' # leaving only the numbers
# create the target path for the file
$targetFolder = Join-Path -Path $destinationRoot -ChildPath $numName
# create that subfolder if it does not already exist
$null = New-Item -Path $targetFolder -ItemType Directory -Force
# now, move the file
$file | Move-Item -Destination $targetFolder
}
Seeing your screenshots, this might be a better approach for you.
$destinationRoot = 'O:\SPG\G\SomeWhere' # enter the root folder destination path here
# get a list of target folders for the files to be moved to and create a lookupHashtable from their names
$targets = #{}
Get-ChildItem -Path $destinationRoot -Directory | Where-Object {$_.Name -match '(\d+)'} | ForEach-Object {
$targets[$matches[1]] = $_.FullName # key is the number, value is the directory fullname
}
# get a list of files to move
$filesToMove = Get-ChildItem -Path 'O:\SCAN\SecSur' -Filter '*.pdf' -File | Where-Object {$_.Name -match '\d+'}
foreach ($file in $filesToMove) {
$numName = $file.BaseName -replace '\D+' # leaving only the numbers
# see if we have a target folder with that same number in its name
if ($targets.ContainsKey($numName)) {
$targetFolder = $targets[$numName]
Write-Host "Moving file $($file.Name) to $targetFolder"
$file | Move-Item -Destination $targetFolder
}
else {
Write-Warning "Could not find a destination folder for file $($file.Name).."
}
}

Copy files into newly created folders on partial filename match

Hi all reaching out because I've reached the limits of my powershell knowledge.
I have a directory that has over 200,000 files, I need to copy all files that have a partial match to the filename into folders that I have already created using this script
Set-Location "C:\Users\joshh\Documents\Testing Environment"
$Folders = Import-Csv "C:\Users\joshh\Documents\Weichert.csv"
ForEach ($Folder in $Folders) {
New-Item "myfilepathhere\$($Folder.folderName)" -type directory
}
UPDATED:
Here is a sample of the filenames:
TH-246-02050-LOL-SQ-ALT2.png
TH-246-02050-WHT-H.png
TH-247-02050-EMB-LOD.png
TH-246-02050-LOL-H-ALT2.png
TH-246-02050-LOL-SQ.png
TH-246-02050-LOL-SQ-ALT.png
TH-247-02050-EMB-LOD-ALT.png
TH-247-02050-EMB-LOL.png
TH-247-02050-EMB-LOL-ALT.png
TH-247-02050-LOD-H.png
Above is an example of what the filenames look like, I need to copy all files containing -EMB- and move them into folders in another directory that match the first 12 characters of that filename (ex. TH-247-02050)
UPDATED:
And if a folder doesn't exist create a folder with the first 12 characters of the filename.
Mind you the first 12 characters have many variants some start with RM, KW, etc.
This is what I have so far and what I know but I know the Move-Item portion isn't exactly what I want it to do
$source = "targetPath"
$destination = "targetPath2"
$embFiles = #(Get-ChildItem ${source}/*EMB* -File | Select-Object -ExpandProperty FullName)
foreach($file in $embFiles) {
if($file | Where-Object { $_ -clike "*EMB*" }){
Move-Item -Path $source -Destination $destination
}
}
Any and all help would be GREATLY appreciated!
Here is one way you could do it:
Get all files that contain -EMB- in their names: -Filter *-EMB-* -File.
Group all this files by everything before -EMB-, here we can use Group-Object -AsHashTable and a calculated expression using Regex.Match. See https://regex101.com/r/iOoBJS/1 for details.
Loop through the Keys of the hash table, each Key will be the Name Destination folder of the group of files (i.e.: TH-247-02050).
Join the destination path ($destinationPath2) with the name of the destination folder ($folder), here we can use Join-Path and check if this joined path exists, if it doesn't, create a new folder with New-Item.
Lastly, we can move all the files (the Values of each Key from the hash table) to their corresponding destination.
$source = "targetPath"
$destination = "targetPath2"
$map = Get-ChildItem $source -Filter *-EMB-* -File | Group-Object -AsHashTable -AsString {
[regex]::Match($_.BaseName, '(?i).+(?=-EMB-)').Value
}
foreach($folder in $map.Keys) {
$d = Join-Path $destination -ChildPath $folder
$d = New-Item $d -ItemType Directory -Force
# -WhatIf can be removed once you have checked the script is doing what you want
$map[$folder] | Move-Item -Destination $d -WhatIf -Verbose
}
-AsString is needed in Windows PowerShell due to a bug.

compare 2 folders and copy the difference in the 3rd folder with the same folder structure recursively using Powershell

Here what I'm trying to do and this is my second post on the same issue, I'm trying to compare 2 folders and copy the difference in the 3rd folder with the same folder structure recursively.
Here is the Powershell script which I receive from an another post (how to copy updated files to new folder and create directory as source if not exist?). This code does everything I need but I have one problem, with the -passthru arguement, it take the file which ever is new in the "folder 2" as well. What I need is what ever files are different in Folder1 from Folder2 alone to be copied over to Folder3. I tried and could not make it work. Any help will be great.
$newdir = "M:\Technical\foldercompare\folder1"
$olddir = "M:\Technical\foldercompare\folder2"
$diffdir = "M:\Technical\foldercompare\folder3"
#Get the list of files in oldFolder
$oldfiles = Get-ChildItem -Recurse -path $olddir | Where-Object {-not ($_.PSIsContainer)}
#get the list of files in new folder
$newfiles = Get-ChildItem -Recurse -path $newdir | Where-Object {-not ($_.PSIsContainer)}
Compare-Object $oldfiles $newfiles -Property LastWriteTime,Length -Passthru | sort LastWriteTime | foreach {
$fl = (($_.Fullname).ToString().Replace("$olddir","$diffdir")).Replace("$newdir","$diffdir")
$dir = Split-Path $fl
If (!(Test-Path $dir)){
mkdir $dir
}
copy-item $_.Fullname $fl
}

Move File to new location where Parent Folder Name Matches

Problem
I am working on a rollback feature in my application in which I copy the files from a backup/rollback directory to a destination folder. As simple as that sounds, this is where it gets complicated. Due to all the files sharing the same or similar name, I used the parent folder as the anchor to help enforce unique locations.
I want to essentially recursively search a directory and wherever a folder name matches a parent directory of an object, paste a copy of the object into that folder, overwriting whatever file(s) share a name with said object.
A more visible way to represent this would be:
$Path = C:\Temp\MyBackups\Backup_03-14-2017
$destination = C:\SomeDirectory\Subfolder
$backups = GCI -Path "$Path\*.config" -Recursive
foreach ($backup in $backups) {
Copy-Item -Path $backup -Destination $destination | Where-Object {
((Get-Item $backup).Directory.Name) -match "$destination\*"
}
}
However, the above doesn't work and none of my research is finding anything remotely similar to what I'm trying to do.
Question
Does anyone know how to Copy an Item from one location to another in which the parent folder of the copied item matches a folder in the destination using PowerShell?
Enumerate the backed-up files, replace the source base path with the destination base path, then move the files. If you only want to replace existing files, test if the destination exists:
Get-ChildItem -Path $Path -Filter '*.config' -Recursive | ForEach-Object {
$dst = $_.FullName.Replace($Path, $destination)
if (Test-Path -LiteralPath $dst) {
Copy-Item -Path $_.FullName -Destination $dst -Force
}
}
If you want to restore files that are missing in the destination make sure to create missing directories first:
Get-ChildItem -Path $Path -Filter '*.config' -Recursive | ForEach-Object {
$dst = $_.FullName.Replace($Path, $destination)
$dir = [IO.Path]::GetDirectoryName($dst)
if (-not (Test-Path -LiteralPath $dir -PathType Container)) {
New-Item -Type Directory -Path $dir | Out-Null
}
Copy-Item -Path $_.FullName -Destination $dst -Force
}
You may be over thinking this. If you are backing up a web.config file from a website, I'd highly advise using the SiteID as the backup folder. Then simply utilize this as the means to find the right folder to copy the web.config file to when you want to rollback.
Ideally when working with any group of items (in this instance websites) try to find a unique identifier for the items. SiteIDs are ideal for this.
$Path = C:\Temp\MyBackups\Backup_03-14-2017 #In this directory store the web.config's in directories that match the SiteID of the site they belong to
#For example, if the site id was 5, then the full backup directory would be: C:\Temp\MyBackups\Backup_03-14-2017\5
$backups = Get-ChildItem -Path $Path -Include *.config -Recurse
foreach ($backup in $backups)
{
$backupId = $backup.Directory.Name
$destination = (Get-Website | where {$_.id -eq $backupId}).physicalPath
Copy-Item -Path $backup -Destination $destination
}

Moving jpg's using powershell script with variable directories

I am writing a scheduled script that will move images (jpg's) from one location to another.
The problem is that a parent directory is variable in name while the end directory is fixed.
If robocopy would do this I would be happy: robocopy C:\temp\pcbmodel**\defect c:\test**\defect*. But it doesn't
For example this will almost work:
foreach ($i in Get-ChildItem C:\temp\pcbmodel\*\*\defect -recurse)
{
if ($i.CreationTime -lt ($(Get-Date).AddMonths(0)))
{
write $i.FullName
Copy-Item $i.FullName C:\Test
}
}
The problem is that the files are copied in c:\test but the ** path isn't. And the * path I need as it will change for each customer.
Some suggestions would be nice,
Bert
This should put you on the right path to getting a working solution. The important part is the Resolve-Path cmdlet which takes a -Relative parameter: http://technet.microsoft.com/en-us/library/hh849858.aspx. new-Item -Force merely tell it to create a folder structure if required.
# $OldRoot = 'Top-level of old files'
# $DestRoot = 'Top-level of destination'
# Go to old root so relative paths are correct
Set-Location $OldRoot
# Get all the images, then for each one...
Get-ChildItem -Recurse -Include "*.jpeg", "*.jpg" |
ForEach-Object {
# Save full name to avoid issues later
$Source = $_.FullName
# Construct destination filename using relative path and destination root
$Destination = '{0}\{1}' -f $DestRoot, (Resolve-Path -Relative -Path:$Source).TrimStart('.\')
# If new destination doesn't exist, create it
If(-Not (Test-Path ($DestDir = Split-Path -Parent -Path:$Destination))) {
New-Item -Type:Directory -Path:$DestDir -Force -Verbose
}
# Copy old item to new destination
Copy-Item -Path:$Source -Destination:$Destination -Verbose
}
How about something like that:
$step1 = get-childitem C:\temp\pcbmodel\
$Else = #FILETYPE YOU DO NOT WANT
$step1 | foreach {get-childitem -recurse -exclude "Directories", $ELSE | where {$_.Creationtime -lt ($(get-date).Addmonths(0))}
}
Hope I got it right and this helps :)
EDIT: Work with -Exclude and -Include parameters they help a lot :)
EDIT2: Ofc, I overread something, sorry for the many edits - you are just looking for jpg files.
$step1 | foreach {get-childitem -recurse -include *.jpg, *.jpeg | where {$_.Creationtime -lt ($(get-date).Addmonths(0))}
Another idea:
Don't try it on your C: disk. It will need forever. But if u have permissions for every directory and subdirectory in a certain path and the subdirectory amount is not enormous it should work.
I created a directory "New Directory" on C: and another "New Directory" in this directory. And ofc, another "New Directory" in the directory. and lastly I created a file with the name "superduper.txt" --> C:\New Directory\New Directory\New Directory\superduper.txt
(get-childitem C:\).FullName | where {(Get-ChildItem $_).FullName | where {(get-childitem $_ -Recurse).FullName -match "superduper"}}
Result:
C:\New Directory
I think it won't be fast and you would need 2 of these to achieve your goal, I guess it should work.
I'm trying to copy all subfolders and their files from one folder into another, without copying their parent folder as well.
For example, in C:\test with some subfolders containing files, copy to D:\test without it being D:\test\test\subfolders.