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).."
}
}
Related
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.
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 directory of information that is separated into document numbers so each folder that contains documents starts with DOC-######-NameOfDocument. The thing I am trying to do is create a PowerShell script that will search a directory for any folders with a specified document number and then take the contents of that folder, move it up one level, and then delete the original folder (which should now be empty).
Below is the closest I have gotten to my intended result.
$Path = "filepath"
$Folders = Get-ChildItem -Filter "DOC-#####*" -Recurse -Name -Path $Path
$companyID = "######"
foreach ($Folder in $Folders){
$filepath = $Path + $Folder
$Files = Get-ChildItem -Path $filepath
$imagesourc = $filepath + $companyID
$imageDest = $filepath.Substring(0, $filepath.LastIndexOf('\'))
if (Test-Path -Path $imagesourc){
Copy-Item -Path $imagesourc -Destination $imageDest -Recurse
}
foreach ($File in $Files){
$Parent_Directory = Split-Path -Path $File.FullName
$Destination_Path = $filepath.Substring(0, $filepath.LastIndexOf('\'))
Copy-Item -Path $File.FullName -Destination $Destination_Path -Recurse
if ($null -eq (Get-ChildItem -Path $Parent_Directory)) {
}
}
Remove-Item $filepath -Recurse
}
This does what I need but for whatever reason I can't Devine, it will not work on .HTM files. Most of the files I am moving are .html and .htm files so I need to get it to work with .htm as well. The files with .HTM will not move and the folder won't be deleted either which is good at least.
Try using this:
$ErrorActionPreference = 'Stop'
$fileNumber = '1234'
$initialFolder = 'X:\path\to\folders'
$folders = Get-ChildItem -Path $initialFolder -Filter DOC-$fileNumber* -Force -Directory -Recurse
foreach($folder in $folders)
{
try
{
Move-Item $folder\* -Destination $folder.Parent.FullName
Remove-Item $folder
}
catch [System.IO.IOException]
{
#(
"$_".Trim()
"File FullName: {0}" -f $_.TargetObject
"Destination Folder: {0}" -f $folder.Parent.FullName
) | Out-String | Write-Warning
}
catch
{
Write-Warning $_
}
}
Important Notes:
Move-Item $folder\* will move all folder contents recursively. If there are folders inside $folder, those will also be moved too, if you want to target folders which only have files inside, an if condition should be added before this cmdlet.
Try {...} Catch {...} is there to handle file collision mainly, if a file with a same name already exists in the parent folder, it will let you know and it will not be moved nor will the folder be deleted.
-Filter DOC-$fileNumber* will capture all the folders named with the numbers in $fileNumber however, be careful because it may capture folders which you may not intent to remove.
Example: If you want to get all folders containing the number 1234 (DOC-12345-NameOfDocument, DOC-12346-NameOfDocument, ...) but you don't want to capture DOC-12347-NameOfDocument then you should fine tune the filter. Or you could add the -Exclude parameter.
-Force & -Directory to get hidden folders and to target only folders.
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 several zip files which have generated names like 21321421-12315-sad3fse-23434fg-ggfsd which doesn't help to identify the content of the zip.
I need a script, which unzips it and then looks for a pdf file with a partly-generated & static name eg asdawd-ersrfse-231-Formular2311.
After that it should create a folder with the name of the pdf file and unzip all zip-file content into this folder.
So far I only have to snippets that work after each other, but I'm still stuck.
$shell = new-object -com shell.application
$CurrentLocation = get-location
$CurrentPath = $CurrentLocation.path
$Location = $shell.namespace($CurrentPath)
# Find all the Zip files and Count them
$ZipFiles = get-childitem -Path "C:\Install\NB\Teststart" *.zip
$ZipFiles.count | out-default
# Set the Index for unique folders
$Index = 1
# For every zip file in the folder
foreach ($ZipFile in $ZipFiles) {
# Get the full path to the zip file
$ZipFile.fullname | out-default
# Set the location and create a new folder to unzip the files into - Edit the line below to change the location to save files to
$NewLocation = "C:\Install\NB\Testfinal\$Index"
New-Item $NewLocation -type Directory
# Move the zip file to the new folder so that you know which was the original file (can be changed to Copy-Item if needed)
Copy-Item $ZipFile.fullname $NewLocation
# List up all of the zip files in the new folder
$NewZipFile = get-childitem $NewLocation *.zip
# Get the COMObjects required for the unzip process
$NewLocation = $shell.namespace($NewLocation)
$ZipFolder = $shell.namespace($NewZipFile.fullname)
# Copy the files to the new Folder
$NewLocation.copyhere($ZipFolder.items())
# Increase the Index to ensure that unique folders are made
$Index = $Index + 1
}
Get-ChildItem -Path "C:\Install\NB\Testfinal" -Include "*.pdf" -Recurse | ForEach-Object {
$oldFolder = $_.DirectoryName
# New Folder Name is .pdf Filename, excluding extension
$newFolder = $_.Name.Substring(0, $_.Name.Length - 4)
# Verify Not Already Same Name
Write-Host "Rename: $oldFolder To: $newFolder"
Rename-Item -NewName $newFolder -Path $oldFolder
}
Along the same lines as your own script, firstly extract the zips and then rename the extracted folder to the same as the pdf:
$SourceDir = 'C:\Install\NB\Teststart'
$ExtractDir = 'C:\Install\NB\Testfinal'
# Extract each zip to a folder with the same name as the zip file (BaseName)
Get-ChildItem (Join-Path $SourceDir *.zip) | foreach {
Expand-Archive $_.FullName -DestinationPath (Join-Path $ExtractDir $_.BaseName)
}
# Rename the PDF's parent folder to the same as the PDF
Get-ChildItem (Join-Path $ExtractDir *.pdf) -Recurse | foreach {
Rename-Item -Path $_.Directory.FullName -NewName $_.BaseName
}
This should do and it's much simpler than what you have. It relies on .NET 4.5 which you should have on your server already:
[Reflection.Assembly]::LoadWithPartialName('System.IO.Compression.FileSystem')
# Get all the zip files in the root of the script, change $PSScriptRoot accordingly
Get-ChildItem -Path $PSScriptRoot -Filter *.zip -Recurse | ForEach-Object {
# Open the archive for reading
$zip = [IO.Compression.ZipFile]::OpenRead($_.FullName)
# Find the name of the PD file from the archive entries
$archiveName = $zip.Entries | `
Where-Object { [System.IO.Path]::GetExtension($_.FullName) -eq '.pdf' } | `
Select-Object #{N = "BaseName"; E = {[System.IO.Path]::GetFileNameWithoutExtension($_.FullName)}} |
Select-Object -Expand BaseName
# Close the zip file
$zip.Dispose()
# Use the native Expand-Archive to unzip the archive
# Ammand $PSScriptRoot to the destination base path if needed
Expand-Archive -Path $_.FullName -DestinationPath (Join-Path $PSScriptRoot $archiveName)
}