Windows - Create Folder and sub folder based on date of filename - powershell

I have a folder with a number of pdf files whereby the last 8 characters is the date of the document in the format YYYYMMDD, samples are:
File_YYYYMMDD
Test_Ref_YYYYMMDD
file.nr_YYYYMMDD
I am looking for a way to create a folder structure based on last 8 characters:
OutputFolder\Subfolder YYYY\Subfolder MM\File name remains the same.
Thanks.

For this you need to iterate through the source folder where the pdf files are, and using their BaseName (= filename without extension), get the year and month part.
Having those, creating the folder structure is a breaze in PowerShell:
$sourceFolder = 'D:\OriginalPath' # the path where your pdf files are
$destination = 'D:\PdfFiles' # the folder where the new folder structure should be created
# get the pdf files that have a basename ending with 8 digits
Get-ChildItem -Path $sourceFolder -Filter '*.pdf' -File |
Where-Object { $_.BaseName -match '\d{8}$' } |
ForEach-Object {
# split the year and month part of the file's basename
$match = $_.BaseName -match '(\d{4})(\d{2})\d{2}$'
$year, $month = $matches[1..2]
# construct the folder path
$targetFolder = Join-Path -Path $destination -ChildPath "$year\$month"
# if the target folder does not exist, create it
if (!(Test-Path -Path $targetFolder -PathType Container)) {
$null = New-Item -Path $targetFolder -ItemType Directory
}
# you probably now want to copy or move the file in that target folder
$_ | Copy-Item -Destination $targetFolder
}
Example of the source folder:
D:\ORIGINALPATH
File_20200201.pdf
File_20200327.pdf
Test_Ref_20191127.pdf
After running the code:
D:\PDFFILES
+---2019
| \---11
| Test_Ref_20191127.pdf
|
\---2020
+---02
| File_20200201.pdf
|
\---03
File_20200327.pdf

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).."
}
}

Archiving files according to date in file name. Powershell script

I am trying to make simple powershell script that is archiving files coming in daily. Every file has date at the beggining of its name, for example: 20211220_Something.csv, 20211220_SomethingElse.txt, 20211219_Something.csv, 20211219_SomethingElse.txt etc...
I would like to make script that collects all files with extensions (*.txt, *.csv, *.xslx) from specifict directories which are:
\\Main\Files and \\Main\Files\SecondaryFiles
and archives all files with above extensions to for example \\Main\Files\archive\2021\12\20.12.zip
where 2021, 12 and 20.12 are elements of date provided in file name prefix. Inside 20.12.zip we have all files from \\Main\Files with directory named "SecondaryFiles" in which theres all files from the \\Main\Files\SecondaryFiles. After archiving i would like to delete all the files that i just zipped.
Right now i have this piece of code which loop through all files in the \Main\ dir and extracts date prefix. I have tried using [Datetime]::parseexact() method but it doesnt work since my loop returns whole path. Anybody has any idea how to approach this?
$Date = Get-Date
$Day = $Date.Day
$Month = Date.Month
$Year = $Date.Year
$directoryPath = "\\Main\Files\archive'"+$Year+"\"+$Month
$files = Get-ChildItem -Path "\\Main\Files" -Include *.txt, *.csv, *.xlsx -Recurse
for ($i=0; $i -lt $files.Count; $i++){
$temp = $files[$i].FullName.split("_")[1]
}
if(!Test-Path -path $directoryPath){
New-Item -ItemType directory -Path $directoryPath
}
Compress-Archive -Path "\\Main\Files", "\\Main\Files\*.txt", "\\Main\Files\*.csv", "\\Main\Files\*.xlsx", "\\Main\Files\SecondaryFiles\*.txt", "\\Main\Files\SecondaryFiles\*.csv", "\\Main\Files\SecondaryFiles\*.xlsx" -Update -DestinationPath "\\Main\Files\archive\$Year\$Month\$Day.$Month.zip"
Then i am removing items from the original directory.
Also one thing worth mentioning is that I cant be sure if folder contains only files from todays date. So script should work fine when theres files from like all week lets say 20211214 till 20211220.
So again i would like to Compress-Archive files like i did above but instead todays date the path would contain extracted date from file name prefix.
Use Group-Object to group all files having the same date prefix together and use that to create the output subdirectories, the final .zip file and also to remove the original files after zipping.
$sourcePath = '\\Main\Files'
$destination = '\\Main\Files\archive'
Get-ChildItem -Path $sourcePath -Include '*.txt', '*.csv', '*.xlsx' -Recurse |
# select only files that start with 8 digits followed by an underscore
Where-Object { $_.BaseName -match '^\d{8}_' } |
# group the files on the date part and loop trhough these groups
Group-Object { $_.BaseName.Substring(0,8) } | ForEach-Object {
# split the date part into variables. Automatic variable $_ represents one Group,
# so we can take that group's Name to split into date parts
$year, $month, $day = $_.Name -split '(\d{4})(\d{2})(\d{2})' -ne ''
# construct the target folder path for the zip file
$targetPath = Join-Path -Path $destination -ChildPath ('{0}\{1}' -f $year, $month)
# create the new sub directory if it does not yet exist
$null = New-Item -Path $targetPath -ItemType Directory -Force
# create the full path and filename for the zip file
$zip = Join-Path -Path $targetPath -ChildPath ('{0}.{1}.zip' -f $day, $month)
# compress the files in the group
Compress-Archive -Path $_.Group.FullName -DestinationPath $zip -Update
# here is where you can delete the original files after zipping
$_.Group | Remove-Item -WhatIf
}
Note I have added switch -WhatIf to the Remove-Item cmdlet. This is a safety switch, so you are not actually deleting anything yet. The cmdlet now only displays what would be deleted. Once you are happy with this output, remove that -WhatIf switch so the files are deleted.

Creating directories using file names in powershell

I have files the following files in my path
ENG0100100VOL-815299004001
ENG0100100VOL-815299004002
ENG0100100VOL-815299004003
ENG0100100VOL-815299004004
ENG0100100VOL-815300004001
ENG0100100VOL-815300004002
ENG0100100VOL-815300004003
ENG0100100VOL-815300004004
I want to
create unique folder(s) with the prefix "VOL-" followed by 6 digits identifier and move the respective files of the package to that folder (VOL-81529 and VOL-81530). Ideally its 4 files per folder. NOTE: These values may change overtime and the file numbers are not fixed.
Folder VOL-81529 should contain files
ENG0100100VOL-815299004001
ENG0100100VOL-815299004002
ENG0100100VOL-815299004003
ENG0100100VOL-815299004004
Folder VOL-81530 should contain files
ENG0100100VOL-815300004001
ENG0100100VOL-815300004002
ENG0100100VOL-815300004003
ENG0100100VOL-815300004004
Traverse through the folders and unzip the file with only suffix 002 and 004 (last three digits) of the 12 digits. And remove all the package files from the folder after unzipping
Folder VOL-81529
ENG0100100VOL-815299004002
ENG0100100VOL-815299004004
Folder VOL-81530
ENG0100100VOL-815300004002
ENG0100100VOL-815300004004
I have little knowledge in coding, I have written this and its requires some additional inputs
$files = Get-ChildItem "\My Documents\Path\files" | ForEach-Object{$_.Tostring().substring(14,5)}
$max =$files.count
for($i=0,$i -lt $max, $i++){if ($files[$i] -eq $files[$i+1]) { $value = $files[$i] } else { $value = $files[$i+1]}}
If you only need the files ending with 002 or 004 we will only treat this ones, right?
This code checks for the desired ending of the file and extracts the content of the archives to a subfolder of the target folder. After the extraction it deletes the files. ... and the other files as well. ;-)
$Path = '\My Documents\Path\files'
Get-ChildItem -Path $Path |
ForEach-Object {
$ID = ($_.BaseName -split '-')[1].Substring(0, 5)
$TargetPath = Join-Path -Path $_.Directory -ChildPath ('VOL-' + $ID)
if ( -not (Test-Path -Path $TargetPath)) {
New-Item -Path $TargetPath -ItemType Directory | Out-Null
}
if ($_.BaseName -match '002$|004$') {
$RenamedItem = Rename-Item -Path $_.FullName -NewName ($_.BaseName + '.zip') -PassThru
$DestinationPath = Join-Path -Path $TargetPath -ChildPath $_.BaseName
Expand-Archive -Path $RenamedItem.FullName -DestinationPath $DestinationPath
Remove-Item -Path $RenamedItem.FullName
}
else {
Remove-Item -Path $_.FullName
}
}

Create a folder by date then rename a file with prior date prefix then move file by extension into folder

This is my first time posting so I apologize if the formatting is unclear or incorrect. I'll try my best to have things look better as I go.
I am trying to create a power shell script that will create a folder with the year and prior month.
I then want to move a certain ext or files only into the folder that was created.
My issue right now is that any text file inside Temp or Temp\files will be moved into the folder that gets created.
On top of that the files that were moved already once would move again the following month and the information in the prior folder will be gone.
Is there any way I can have it just move the files that are outside the folder into the new folder?
My other issue now is I would like to create the same date format as a prefix to the text document example: 201902-_Name.txt
I haven't figured out the 2nd part and I kinda sorta figured out the 1st part except for it grabs anything inside temp and moves it to the new folder it creates.
# Get the files which should be moved, without folders
$files = Get-ChildItem 'C:\Temp\' -Recurse | where {!$_.PsIsContainer}
# List Files which will be moved
$files
# Target Folder where files should be moved to. The script will automatically create a folder for the year and month.
$targetPath = 'C:\Temp\files\'
foreach ($file in $files){
# Get year and Month of the file
# I used LastWriteTime since this are synced files and the creation day will be the date when it was synced
$year = $file.LastWriteTime.Year.ToString()
$month = (Get-Date).AddMonths(-1).ToString('MM')
$monthname = (Get-Culture).DateTimeFormat.GetAbbreviatedMonthName($month)
# Out FileName, year and month
$file.Name
$year
$month
$monthname
# Set Directory Path
$Directory = $targetPath + "\" + $year + $month
# Create directory if it doesn't exsist
if (!(Test-Path $Directory)){
New-Item $directory -type directory
}
# Move File to new location
$file | Move-Item -Destination $Directory
}
The by far easiest way to tackle your problem #1 is to have the files moved to a target folder that is NOT inside the source folder.
If that is not what you want, then you need to add an extra test for the Get-ChildItem cmdlet to filter out any files that are in the target folder.
Something like this should work:
$sourcePath = 'C:\Temp\' #'# The folder in which the files to move are
$targetPath = 'C:\Temp\files\' #'# The folder where the files should be moved to
# Get the files which should be moved, without folders and exclude any file that is in the target folder
$files = Get-ChildItem $sourcePath -File -Recurse | Where-Object { $_.FullName -notlike "$targetPath*" }
# for PowerShell version below 3.0 use this:
# $files = Get-ChildItem 'C:\Temp\' -Recurse | Where-Object {!$_.PsIsContainer -and $_.FullName -notlike "$targetPath*"}
# List Files which will be moved
# $files
foreach ($file in $files){
# Get year and Month of the file
# I used LastWriteTime since this are synced files and the creation day will be the date when it was synced
$year = $file.LastWriteTime.Year
$month = (Get-Date).AddMonths(-1).ToString('MM') # last month from current date
$monthname = (Get-Culture).DateTimeFormat.GetAbbreviatedMonthName($month)
# Out FileName, year and month
# $file.Name
# $year
# $month
# $monthname
$dateString = '{0}{1}' -f $year, $month
# Set Directory Path
$Directory = Join-Path -Path $targetPath -ChildPath $dateString
# Create directory if it doesn't exsist
if (!(Test-Path $Directory -PathType Container)){
New-Item $Directory -ItemType Directory | Out-Null
}
# Move File to new location and prepend the date prefix to its name
$targetFile = Join-Path -Path $Directory -ChildPath ('{0}-{1}' -f $dateString, $file.Name)
$file | Move-Item -Destination $targetFile -Force
}
As you can see, by using the same $dateString variable, the Move-Item cmdlet not only moves, but renames the files aswell.
Hope this helps

powershell Script for unzipping in specific named folder

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)
}