Powershell - Move files to existing folders with partially matching names - windows

I have a folder with images that multiple people will be contributing, and I have multiple folders that other people will be creating with additional content, the folder names will be similar but not identical to the file names (see example below). I have tried writing a Powershell script (I'm still a bit green with it) that moves each image to its respective folder but I hit a wall, I'm getting the "Cannot convert 'System.Object[]' to the type 'System.String' required by parameter 'Destination'. Specified method is not supported." I tried converting the destination to a string but had trouble looping through the list of folders. I'm adding the code here in hopes someone will spot the error I made in the code and will point me in the right direction. Thanks in advance for taking the time to help.
$sourceFolder = Get-ChildItem 'C:\Example_Files_Folder' -Recurse -Filter *.png
$targetFolder = Get-ChildItem -Path 'C:\Example_Target_Folder' -Recurse -Directory
foreach ($file in $sourceFolder) {
Where-Object { $targetFolder.Name -cin $sourceFolder.BaseName }
move-Item -Path $file.FullName -Destination $targetFolder.FullName -WhatIf
Write-Output "...copying $file"
}
<# ### Folder Structure
▼sourceFolder #Root Folder containing files
├───►looking inside this folder.png
├───►you should be able to find.png
├───►quite a long list of files.png
├───►matching many folders names.png
└───►that exist inside other folders.png
▼targetFolder #Root Folder
├───▼subfolder01
│ └───►looking inside #this is a folder
├───▼subfolder02
| └───▼subfolder002
| └───▼subfolder0002
| └───►you should be #also a folder
├───▼subfolder03
| └───►quite a long #not a file, just a folder
├───▼subfolder04
| └───►matching many folders #folder
├───▼subfolder05
| └───►sometimes with no matching file name #yep, a folder
├───▼subfolder06
| └─────►that exist within many folders #you get it
└───►subfolder07
### #>

Continuing from my comment ...
You can simplify this to say something like this:
# Target folder tree
$targetFolders = Get-ChildItem -Path 'D:\Temp\Target' -Recurse -Directory
# Results
<#
Directory: D:\Temp\Target
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 17-Feb-21 10:22 Book
d----- 17-Feb-21 10:10 TextData
Directory: D:\Temp\Target\Book
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 17-Feb-21 10:10 Date
#>
# Source folder files to move
Get-ChildItem 'D:\Temp' -Recurse -Filter '*.txt' |
ForEach {
ForEach ($targetFolder in $targetFolders)
{
If ($PSItem.BaseName -match $targetFolder.Name)
{Move-Item -Path $PSItem.FullName -Destination $targetFolder.FullName -WhatIf}
}
}
# Results
<#
What if: Performing the operation "Move File" on target "Item: D:\Temp\Book- Copy (2).txt Destination: D:\Temp\Target\Book\Book- Copy (2).txt".
....
What if: Performing the operation "Move File" on target "Item: D:\Temp\DateData.txt Destination: D:\Temp\Target\Book\Date\DateData.txt".
...
What if: Performing the operation "Move File" on target "Item: D:\Temp\TextData.txt Destination: D:\Temp\Target\TextData\TextData.txt".
#>

Related

share multiple folders with the same name condition

I need everyone's help to get back to the power shell, I currently have a directory tree with a lot of folders you can see the images I borrowed.
enter image description here
I want to share folder "C and F" all directory tree at once with multiple users with view and edit permissions. hope everyone can help. I'm so stupid about this.
Hi khuchatvui and welcome to stackoverflow!
New-SmbShare can be used for creating shared folders.
If I understand correctly, you only want to share folders with a specific name that exist at multiple levels. SMB share names have to be unique, so that will provide a challenge if you want to have a specific sharename
You could partly automate this process by getting prompt for each folder name during the creation:
Solution 1 - prompt for name
$FoldersToShare = Get-ChildItem -Path C:\Tests\ -Recurse | Where-Object { $_.Name -eq 'F' -or $_.Name -eq 'C' } | Select-Object -ExpandProperty FullName
foreach ($folder in $FoldersToShare) {
New-SmbShare -Name (Read-Host -Prompt "Enter the sharename for $($folder)") -Path $folder -ChangeAccess "domain\groupname"
}
If there is no pattern in the folders you want to share, but the names are unique, you could make a list of all the folders you want to share like this:
Solution 2 - create unique folder names
Get-ChildItem -Path c:\tests -Directory -Recurse | Select-Object Name, FullName | Export-Csv -NoTypeInformation -NoClobber -Delimiter ';' -Path C:\Tests\Stackoverflow\FoldersToShare.csv
Then, modify that list using a text editor or Excel to only contain the folders you want to share and use that to loop through New-SmbShare
Finally, use PowerShell to import the contents of the modified csv file and loop through the entries with New-SmbShare to create the shared folders
$FoldersToShare = Import-Csv -Path C:\Tests\Stackoverflow\FoldersToShare.csv -Delimiter ';'
foreach ($folder in $FoldersToShare) {
New-SmbShare -Name $folder.Name -Path $folder.FullName -ChangeAccess "domain\groupname"
}
For my solution, I created the folder structure from your image under C:\Tests
C:\Tests\
A
A1
C
F
A2
C
B
B1
C
B2
C

Moving multiple files to matching name ones in another disk in PowerShell

I was downloading a huge torrent (1.2tb with over 6000 folders) divided in 2 parts, so I placed the 2nd part on the designed place and it was not a problem since the master-folder of the torrent is exactly what was needed. The 1st part master-folder was named with some generic torrent name instead of the name I needed, so instead of renaming the torrent name to "source", which I think would have worked and renamed the currently generic name to "source". In files tab I selected all the files and right-click>relocate all of them and bittorrent simply moved all of the files to the same directory, without any subfolder, and created a mess.
So I have a un-finished backup of this torrent and the files are in place, so my idea was using the un-finished one's name, match with the finished ones and put the finished ones in the un-finished matching name's path folder. I hope that was clear.
I tried to resolve this using PowerShell, but I dont know much, so I came up with this and nothing happens, something is wrong. Anyone knows a solution?
$itemlistA = Get-ChildItem -Path "D:\BitTorrent\" |
ForEach-Object {
$objnameA = $_.Name
$objPathA = $_.FullName
}
$itemlistB = Get-ChildItem -Path "E:\DesiredPath\" -recurse |
ForEach-Object{
$objnameB = $_.Name
$objPathB = $_.FullName
}
ForEach-Object{
if($objnameA -eq $objnameB){
Copy-Item -path $objPathA -Destination $objPathB
Write-Host "ffff Object ($objnameA) new Path ($objPathB) ffff"
}
}
If I'm understanding your intent correctly, the script below will accomplish your goal, assuming your goal is to copy files from a flattened directory into some (potentially) nested directories so that the incoming files overwrite files with matching names.
The O(n^2) performance of the nested loops could be improved with a sort and more efficient search.
You'd need to edit the script's params to reflect your own environment.
param(
$pathToFiles = "$PSScriptRoot\BitTorrent\",
$desiredPath = "$PSScriptRoot\DesiredPath\"
)
$itemlistA = Get-ChildItem -Path $pathToFiles | Select-Object -Property Name, FullName
$itemlistB = Get-ChildItem -Path $desiredPath -Recurse | Select-Object -Property Name, FullName
foreach ($fileA in $itemlistA) {
foreach ($fileB in $itemListB) {
if ($fileB.Name -eq $fileA.Name) {
Copy-Item -path $fileA.FullName -Destination $fileB.FullName -Verbose
break
}
}
}

Find folders by variables and action on them

I am trying to build a script with a variable based on a text file of folder names, find them in one directory and move them to a different directory (or delete them in an additional script).
For example:
$foldernames = Get-Content -Path "C:\Users\Me\Desktop\Folders.txt"
$startpath = "C:\Users\Me\Desktop\Test"
$topath = "C:\Users\Me\Desktop\Moved"
Then find each line item in folder names in the $startpath and if they are found move them to the $topath.
this will do the job. [grin] it fakes reading in your text file of to-move dirs [use Get-Content to do that for real], then gathers a list of dirs in the source location. next, it tests to see if the .Name is in the list of dirs to be moved. finally, it runs Move-Item to move the dir.
note the -WhatIf on the end ... remove that when you are ready to do your live test on your test data set. [grin]
# fake reading in a text file
# in real life, use Get-Content
$ToMoveDirNameList = #(
'test1'
'TuTu'
'GoGoGadget'
'WPF'
)
$SourceDir = $env:TEMP
$DestDir = 'd:\temp'
$SourceDirList = Get-ChildItem -LiteralPath $SourceDir -Directory
foreach ($SDL_Item in $SourceDirList)
{
if ($SDL_Item.Name -in $ToMoveDirNameList)
{
Move-Item -LiteralPath $SDL_Item.FullName -Destination $DestDir -WhatIf
}
}
output ...
What if: Performing the operation "Move Directory" on target "Item: C:\Temp\test1 Destination: D:\temp\test1".
What if: Performing the operation "Move Directory" on target "Item: C:\Temp\TuTu Destination: D:\temp\TuTu".
What if: Performing the operation "Move Directory" on target "Item: C:\Temp\WPF Destination: D:\temp\WPF".
note #2 - there is NO error checking. this will give you lots of red error text if the destination already has a dir with the same name.
note #3 - there is no record of not-found dirs, either. [grin]

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.

File/Directory Path should look like this - Powershell output

I am using a Powershell Script which should create a file that includes the Directory-Order (folder, subfolder, files, etc.):
$path = "golf.de/dgv"
Get-ChildItem -Path $folder -recurse | sort Directory, Name| format-Table -auto $path, Directory, Name | Out-File C:\Users\J.Kammermeier\Desktop\Johannes\testtext.txt
until now the output looks like this
C:\Users\J.Kammermeier\Desktop\Johannes Test-Datei1.txt
C:\Users\J.Kammermeier\Desktop\Johannes Test-Datei2.txt
C:\Users\J.Kammermeier\Desktop\Johannes\Sonstige Datein\Musik WACKEN.txt
but I need it in this order:
.../Johannes Test-Datei1.txt
...Johannes\Sonstige Datein\Musik WACKEN.txt
How to achieve it?
You'll have to mangle the Directory property a bit, using Select-Object and calculated properties:
# Set the path and folder property
$path = "golf.de/dgv"
$folder = "C:\Users\J.Kammermeier\Desktop\Johannes"
# Get the name of the parent folder (the part we want to remove)
$basePath = (Get-Item $folder).Parent.FullName
# Retrieve the files
$files = Get-ChildItem -Path $folder -Recurse
# Select the Name property and then two calculated properties, "Directory" and "Path"
$files = $files |Select-Object #{Name="BaseURL";Expression={"$path"}},
#{Name="Directory";Expression={$_.Directory.FullName.Substring($basePath.Length - 1)}},
Name
# Sort them
$files = $files |Sort-Object Directory, Name
# Formatted output to file
$files | Format-Table -AutoSize | Out-File C:\Users\J.Kammermeier\Desktop\Johannes\testtext.txt
From the details, I guess that you're trying to audit the files for a website, you could combine the Path and Directory properties and fix the back slashes with -replace:
#{Name="URLPath";Expression={"$path/" + $($_.Directory.FullName.Substring($basePath.Length - 1) -replace "\\","/")}}

Resources