I am trying to create a powershell script that does the following:
Move the latest 3 subfolders(a-b-c with files) into a new folder with today's date without moving older files
Security has 3 subfolders generated today along with subfolders from previous days.
$localpath ="D:\Security\"
Get-ChildItem $localpath -Recurse | foreach {
$DateofFile = $_.LastWriteTime.ToShortDateString()
$Todaysfolder = Get-Date $DateofFile -Format yyyMMdd
$targetpath = $Todaysfolder
Create directory if it doesn't exsist
if (!(Test-Path $targetpath))
{
New-Item $targetpath -type directory
}
Get-ChildItem $localpath | Move-Item -Destination $targetpath
}
Process right now is grabbing all files - even the ones that were not created today and grouping them into one folder. This script will run at the end of the day to move those 3 subfolders (A-B-C) into today's folder "20200520" example
There's no need to recurse Get-ChildItem when moving the entire folder - saves you looping through contained files.
$FromPath ="C:\fromtest"
$ToPath = "C:\totest\"+(Get-Date -Format yyyMMdd)
$FoldersToMove = Get-ChildItem $FromPath |
Where-Object{$_.LastWriteTime -ge (Get-Date).date}
#Create directory if it doesn't exsist
if (!(Test-Path $ToPath)){
New-Item $ToPath -type directory
}
foreach ($Folder in $FoldersToMove){
$Folder | Move-Item -Destination $ToPath
}
Related
I have thousands of files of many years and I want to archive these files on year -> month basis. I want to keep latest 2 months of files and older than 2 months should be archived. The catch is to determine the year and month of a specific file I have to do it from the file name.
Filename format : ABCXYZ_20220715.xml
First 4 digits are year (2022), followed by 2 digits of month(07) and 2 digits of day(15).
These files not necessarily were created on the same date as given in the file name. Otherwise it would have been easy for me to achieve this using group by $_.LastWriteTime
Below is the desired output:
Script I wrote to achieve this, but using $_.LastWriteTime and NOT from the file name.
# Two months from the beginning of the month
$today = [datetime]::Today
$maxAge = $today.addMonths(-2)
$SourceFolder = "C:\Temp\sent"
$DestinationFolder = "C:\Temp\Archive"
$filesByMonth = Get-ChildItem -Path $SourceFolder -File |`
where LastWriteTime -LT $maxAge |`
group { $_.LastWriteTime.ToString("yyyy\\MM") }
foreach ($monthlyGroup in $filesByMonth) {
$archiveFolder = Join-Path $DestinationFolder $monthlyGroup.Name
New-Item -Path $archiveFolder -ItemType Directory -Force
$monthlyGroup.Group | Move-Item -Destination $archiveFolder
# $monthlyGroup.Group | Move-Item -Destination $_.fullName.Replace($SourceFolder, $archiveFolder)
#the second $archivefolder is the name for the ZIP file, the extensions is added automatically
Compress-Archive -Path $archiveFolder -DestinationPath $archiveFolder
Remove-Item $archiveFolder -Recurse
}
One solution is the following modification of your code. It just adds the custom "GroupableNameDate" with the date extracted from the files.
# Two months from the beginning of the month
$today = [datetime]::Today
$maxAge = $today.addMonths(-2)
$SourceFolder = "C:\Temp\sent"
$DestinationFolder = "C:\Temp\Archive"
$filesByMonth = Get-ChildItem -Path $SourceFolder -File |`
where LastWriteTime -LT $maxAge |`
Select *,#{
Name="GroupableNameDate"
Expression={
$d = $_.basename -split "_" | Select-Object -Last 1
[System.DateTime]::ParseExact($d,'yyyyMMdd',$null)
}
} |
group { $_.GroupableNameDate.ToString("yyyy\\MM") }
foreach ($monthlyGroup in $filesByMonth) {
$archiveFolder = Join-Path $DestinationFolder $monthlyGroup.Name
New-Item -Path $archiveFolder -ItemType Directory -Force
$monthlyGroup.Group | Move-Item -Destination $archiveFolder
# $monthlyGroup.Group | Move-Item -Destination $_.fullName.Replace($SourceFolder, $archiveFolder)
#the second $archivefolder is the name for the ZIP file, the extensions is added automatically
Compress-Archive -Path $archiveFolder -DestinationPath $archiveFolder
Remove-Item $archiveFolder -Recurse
}
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
I have thousands of files spanning 5 years which I would like to move into year/month folders. The file names all end with
_yyyy_mm_dd_wxyz.dat
I'm looking for ideas on how I can generate such file folders and move the files into the appropriate folders yyyy/mm using the windows command shell.
You'll need a Regular Expression with (capture groups) to extract year/month from the filename.
Assuming the year/month folder should be placed directly in files parent location.
untested with -Version 2
## Q:\Test\2018\07\23\SO_51485727.ps1
Push-Location 'x:\folder\to\start'
Get-ChildItem *_*_*_*_*.dat |
Where-Object {$_.BaseName -match '_(\d{4})_(\d{2})_\d{2}_[a-z]+$'} | ForEach-Object {
$TargetDir = "{0}\{1}" -f $Matches[1],$Matches[2]
if (!(Test-Path $TargetDir)){MD $TargetDir | Out-Null}
$_ | Move -Destination $TargetDir
}
Sample tree /f after running the script on my ramdriive:
PS A:\> tree /F
A:.
├───2017
│ └───07
│ test_2017_07_24_xyz.dat
└───2018
└───07
test_2018_07_24_xyz.dat
I have created this little quick and dirty script.
Things have been put in more variables than strictly needed, they could be combined in a single line, but I feel this adds clarity which I hope help you understand what happens.
As a note, I have used the date the item was last written to (created or edited).
If you want only the date the file was created and not the time the file was last edited, you could change LastWriteTime to CreationTime
#Load all files from the folder you wish to move on
$items = Get-ChildItem -Path "C:\SomeFolder\RestofPathToYourFiles"
foreach($item in $items) {
#Creates variables for year, month and day
$FolderYear = "$($item.LastWriteTime.Year)"
$FolderMonth = "$($item.LastWriteTime.Month)"
$FolderDay = "$($item.LastWriteTime.Day)"
#create variable with the new directory path
$NewPath = $item.Directory.FullName + "\" + $FolderYear + "\" + $FolderMonth + "\" + $FolderDay
#create variable with the new full path of the file
$NewFullPath = $NewPath + "\" + $item.Name
#test if the folder already is created, if not, create it
if((Test-Path -Path $NewPath) -eq $false) {
New-Item -Force -path $NewPath -Type Directory
}
#move the item to the new folder
Move-Item -Path $item.FullName -Destination $NewFullPath -Force
}
At the simplest, I'd do something like the following:
Determine the year and month related to a file
See if a folder exists already. If not, create it
Move the file
Example...
foreach ($file in $(ls .\stuff.txt)) {
$m = $file.LastWriteTime.Month.ToString("00")
$y = $file.LastWriteTime.Year
$dir = "{0}-{1}" -f $y, $m
New-Item -Name $dir -ItemType directory -ErrorAction SilentlyContinue | Out-Null
Move-Item -Path $file.Fullname -Destination $dir
}
I have been given a business requirement to move some files every month into a file folder format similar to the below. I have used powershell before to move files to a folder that already exists and I have read some things about powershell creating and naming folders but is there a way to do it monthly without changing the folder name it creates every month?
Client 1
Reports 2018
01Jan
02Feb
03Mar
etc.
Client 2
Reports 2018
01Jan
02Feb
03Mar
etc.
So ideally, it would be a script that would create a folder for the month it is run and then move all files in a directory that were created/modified that month into their monthly folder.
Is this possible?
some code I have tried:
Get-ChildItem \\test\d$\Reports*.xlsx -Recurse | foreach {
$x = $_.LastWriteTime.ToShortDateString()
$new_folder_name = Get-Date $x -Format yyyy.MM
$des_path = "c:\test\test2\$new_folder_name"
if (test-path $des_path){
move-item $_.fullname $des_path
} else {
new-item -ItemType directory -Path $des_path
move-item $_.fullname $des_path
}
}
::
cd "\\test\d$\Reports\client1"
$fso = new-object -ComObject scripting.filesystemobject
$fso.CreateFolder("\03Mar")
cd "\\test\d$\Reports\client2"
$fso = new-object -ComObject scripting.filesystemobject
$fso.CreateFolder("\03Mar")
repeat for 20 clients but as you could tell this gets hard to keep up with every month since you have to go update the 03 to 04 and mar to april and further on.
EDIT:
As requested the structure is as follows:
Client 1 - folder
Saved Reports - folder
2017 - folder
01Jan - folder
report 1
02Feb - folder
2018 - folder
01Jan - folder
02Feb - folder
Repeating for 20+ clients
Also EDIT:
# Get the files which should be moved, without folders
$files = Get-ChildItem '\\test\d$\Reports\client\saved reports' -Recurse | where {!$_.PsIsContainer}
# List Files which will be moved
$files
# Target Filder where files should be moved to. The script will automatically create a folder for the year and month.
$targetPath = '\\test\d$\Reports\client\2018 client reports'
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 = $file.LastWriteTime.Month.ToString("00")
$monthname = (Get-Culture).DateTimeFormat.GetAbbreviatedMonthName($month)
# Out FileName, year and month
$file.Name
$year
$month
$monthname
# Set Directory Path
$Directory = $targetPath + "\" + $month + $monthname
# 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 above seems to be working, but I have to have this entire block of code for every client and will have to change the year each time, any way to generalize this or not?
Last Edit:
Also -- is there a way to do $month -1 so the folder says the last month name not the current month name?
Thanks,
You will need to use the following Cmdlets:
New-Item -Path $path -Name "$((Get-Date).Month)_$((Get-Date).Year)" -ItemType Directory
Get-ChildItem $directoryWTheFiles -File -Recurse | where {$_.CreationTime.Month -eq ((Get-Date).Month) -or $_.LastWriteTime.Month -eq ((Get-Date).Month)} | Move-Item -Destination $dest
I haven't tested it, but this should get you to a 70% completion on that script.
Here is what I have so far... it is just an expansion on Roque Sosa's answer
$folderName = "$(Get-Date -format MM)$(Get-Date -format MMM)"
New-Item -Path $path -Name $folderName -ItemType Directory
$destination = "$path/$folderName/"
Get-ChildItem $directoryWTheFiles -File -Recurse |
Where-Object {$_.LastWriteTime.Month -eq ((Get-Date).Month) -and $_.LastWriteTime.Year -eq ((Get-Date).Year)} |
Move-Item -Destination $destination
This at least gets the folder name formatted correctly as you have shown above. Then it also moves the files that match the month and year of the lastwritetime attribute.
Once you give me the structure of the client folders you are searching I can update more specific to your situation.
I managed to find an answer to this, thank you for your help.
I have updated my post with the code.
Thanks again
Wrote the following code to move files to a specific Year-Month folder on a drive. However, I would also like to zip the folder I wrote to at the end of the operation. How do I do that?
# Get the files which should be moved, without folders
$files = Get-ChildItem 'D:\NTPolling\InBound\Archive' -Recurse | where {!$_.PsIsContainer}
# List Files which will be moved
# $files
# Target Filder where files should be moved to. The script will automatically create a folder for the year and month.
$targetPath = 'D:\SalesXMLBackup'
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 = $file.LastWriteTime.Month.ToString()
# Out FileName, year and month
$file.Name
$year
$month
# 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 intention is to move these files into a folder and zip them and archive them for later use. So I will schedule this once a month to run for the previous month
If you're using PowerShell v5 then you can use the Compress-Archive function:
Get-ChildItem $targetPath | Compress-Archive -DestinationPath "$targetPath.zip"
This will compress D:\SalesXMLBackup to D:\SalesXMLBackup.zip
This is the code I am using for unzipping all the files in a directory. You just need to modify it enough to zip instead of unzipping.
$ZipReNameExtract = Start-Job {
#Ingoring the directories that a search is not require to check
$ignore = #("Tests\","Old_Tests\")
#Don't include "\" at the end of $loc - it will stop the script from matching first-level subfolders
$Files=gci $NewSource -Fecurse | Where {$_.Extension -Match "zip" -And $_.FullName -Notlike $Ignore}
Foreach ($File in $Files) {
$NewSource = $File.FullName
#Join-Path is a standard Powershell cmdLet
$Destination = Join-Path (Split-Path -parent $File.FullName) $File.BaseName
Write-Host -Fore Green $Destination
#Start-Process needs the path to the exe and then the arguments passed seperately.
Start-Process -FilePath "C:\Program Files\7-Zip\7z.exe" -ArgumentList "x -y -o $NewSource $Destination" -Wait
}
}
Wait-Job $ZipReNameExtract
Receive-Job $ZipReNameExtract
Let me know if it helps.
The UnderDog...