Script to organize folders - powershell

I need to organize many folders, this folders must have some kind of format in their names. I need to rename them and put them like this:
parent folder 2nd name + "_" + 2 digits + "_" + folder name
This needs to happen with every folder where the .ps1 is executed.
Here are some better explanations, here we have some folders:
00_00_Master folder
+- 00_01_Activities
`- 00_02_Schedules
+- 02_00_dir1
`- 02_01_dir2
As you can see, the subfolders inside 00_02_Schedules start with the 2nd "name".
Etc, with all folders (never files). Now for a better explanation, I added (it will happen a lot) 3 more folders inside 00_02_Schedules. It should be like this (before and after executing .ps1)
Before:
...
`- 00_02 Schedules
+- 02_00_dir1
+- 02_01_dir2
+- dir3
+- 12345dir4
`- 01_01_dir5
After:
...
`- 00_02 Schedules
+- 02_00_dir1
+- 02_01_dir2
+- 02_02_dir3
+- 02_03_dir4
`- 02_04_dir5
I will try to explain a little (I put all the code I had, even when I think some is useless now)
$var1 = 0
$var2 = 0
# It always has to work in the same place, never with a fixed path like
# C:\Users\etc\etc
$arr1 = Get-ChildItem -Path .\ -Directory
$arr2 = New-Object System.Collections.ArrayList
[string]$dir = Get-Location
$ruta = $dir | Split-Path -Leaf
# Did these 2 things because I needed the folder's name, I didn't know how
# to do it better
# I think we can say this following code is useless now, I think I tried to do
# an array of arrays so I could cleave the name
foreach ($Name in $arr1) {
[void]$arr2.Add($Name)
}
foreach ($Directory in $arr1) {
if ($Directory -match '^[0-9].*') {
Rename-Item \$Directory -NewName { [string]($_.Name).Substring(5) }
# This is the part I've being having most problems with. I may say the
# code works...as long a folder's name doesn't start with a number,
# because it enters inside the loop and many error logs pops out. Also
# it renames the folders previously renamed like 00_01_00_01, when it's
# supposed to delete before rename them.
# Also, I think it won't work if the numeric extension isn't equal to 5.
}
}
$arr3 = $ruta -split '_'
$foldername = $arr3[1]
# Works, It splits and give me the second "piece" between the 2 "_"
foreach ($Directory in $arr1) {
Rename-Item "$Directory" -NewName $("$foldername" + "_" + "$var1" + "$var2" + "_" + $Directory.Name)
# I think it should have been: (I put it that way before)
# $("$foldername" + "_" + "$var2" + "$var1" + "_" + $Directory.Name)
# but if I did that way, it renamed them like 10,20,30 instead of 01,02,03 etc.
$var++
if ($var1 = 9) {
$var1 = 0
$var2++
# I suppose is not fancy, but in the result output I can see 2 numbers, not 1
}
}
One more thing, is it possible to transform this (fixed) code to work in all folders in a recursive way? (would save a lot of work and executions).

With this initial situation:
> tree
└───00_00_Master folder
├──00_01_Activities
└──00_02_Schedules
├──01_01_dir5
├──02_00_dir1
├──02_01_dir2
├──12345dir4
└──dir3
This script (adapt the Set-Location to fit your environment):
## Q:\Test\2017\08\18\SO_45750567.ps1
Function Test-DirSchema ($DirName) {
# assume parent is already processed and complies with schema
$ThisDir = GI $DirName
# Get parent's second number for our first
$First = $ThisDir.Parent.Name.Split('_')[1]
If ($ThisDir.Name -Match "^$($First)_\d{2}_.*") {
"{0} : {1} complies with schema" -f $First,$ThisDir.Name
} else {
$Highest = (gci "$($ThisDir.Parent)\$($First)_??_*"|Sort|Select -Last 1).Name
$Second = [int]($Highest -Replace('^\d{2}_(\d{2})_.*','$1'))
$Second = ($Second + 1).ToString('00')
"{0} : {1} does not comply with schema" -F $First,$ThisDir.Name
$NewName = "{0}_{1}_{2}" -F $First, $Second,$ThisDir.Name.TrimStart('0123456789_')
"new: {0}" -F $NewName
$ThisDir | Rename-Item -NewName $NewName
}
"----"
}
Set-Location "A:\00_00_Master folder"
Get-ChildItem "[0-9][0-9]_*" -Rec |
Where-Object {$_.PSIsContainer} |
ForEach-Object {Test-DirSchema $_.FullName}
with this output:
> Q:\Test\2017\08\18\SO_45750567.ps1
02 : 01_01_dir5 does not comply with schema
new: 02_02_dir5
----
02 : 02_00_dir1 complies with schema
----
02 : 02_01_dir2 complies with schema
----
02 : 12345dir4 does not comply with schema
new: 02_03_dir4
----
02 : dir3 does not comply with schema
new: 02_04_dir3
----
Will have this end situation:
> tree
└───00_00_Master folder
├──00_01_Activities
└──00_02_Schedules
├──02_00_dir1
├──02_01_dir2
├──02_02_dir5
├──02_03_dir4
└──02_04_dir3
The order is, due to different sorting, not exactly yours.
If there is no previous number it will start with 01.

Get-ChildItem -Directory -Recurse | ForEach { #Get All Directories
$a = 0 #Reset Indexing to 01
Get-ChildItem $_.FullName -File | ForEach {
$ParentFolderSecond = ($_.Directory.Parent.Name -split '_')[1] #Second part of folder's parent
$Number = $a++ #Number incrementing by 1
$FolderName = $_.Directory.Name #folder's name
"$($ParentFolderSecond)_$($Number.ToString("00"))_$($FolderName)$($_.Extension)" #put this into a rename-item if it works OK.
}
}
Does this match your specs?
it should work recursively as is but I think i'm missing something in the parent folder part.
it won't rename anything, just spit out a list of what the file names 'will' be.

I would probably do something like this:
Get-ChildItem -Recurse -Directory | Where-Object {
# find schedule directories below the current working directory
$_.Name -match '^\d{2}_(\d{2})_Schedules$'
} | ForEach-Object {
$prefix = $matches[1]
$pattern = "^${prefix}_(\d{2})_"
# enumerate subfolders of schedule directory
$folders = Get-ChildItem $_.DirectoryName -Directory
# find highest existing index of folders that already conform to the
# name schema
$lastindex = $folders |
Where-Object { $_.Name -match $pattern } |
ForEach-Object { [int]$matches[1] } |
Sort-Object |
Select-Object -Last 1
$nextindex = if ($lastindex) { $lastindex + 1 } else { 0 }
# rename folders that don't conform to the name schema starting with the
# index after the highest existing index (or 0 if there was none), remove
# digits and underscores from beginning of folder name
$folders | Where-Object {
$_.Name -notmatch $pattern
} | Rename-Item -NewName {
$_.Name -replace '^\d(?:[_\d]*)', "${prefix}_${script:nextindex}_"
${script:nextindex}++
}
}

Related

Create directories and subdirectories in Powershell to move files

I am trying to create directories and subdirectories based on the names of existing files. After that I want to move those files into the according directories. I have already come pretty far, also with the help of
here and here, but I am failing at some point.
Existing Test Files Actually about 5000 files
Folder structure This is how it should look like afterwards
MM0245AK625_G03_701.txt
MM\MM0245\625\G03\MM0245AK625_G03_701.txt
MM0245AK830_G04_701.txt
MM\MM0245\830\G04\MM0245AK830_G04_701.txt
VY0245AK_G03.txt
VY\VY0245\VY0245AK_G03.txt
VY0245AK_G03_701.txt
VY\VY0245\G03\VY0245AK_G03_701.txt
VY0245AK625_G03.txt
VY\VY0245\625\VY0245AK625_G03.txt
VY0245AK625_G03_701.txt
VY\VY0245\625\G03\VY0245AK625_G03_701.txt
VY0345AK625_G03_701.txt
VY\VY0345\625\G03\VY0345AK625_G03_701.txt
Code for creating those files is at the end of this post.
As you can see, the files do match some kind of pattern, but not consistently. I use multiple copies of my code with different 'parameters' to sort each type of filepattern, but there gotta be a more streamline way.
Existing code
$dataPath = "$PSScriptRoot\Test"
#$newDataPath = "$PSScriptRoot\"
Get-ChildItem $dataPath -Filter *.txt | % {
$g1 = $_.BaseName.Substring(0, 2)
$g2 = $_.BaseName.Substring(0, 6)
$g3 = $_.BaseName.Substring(8, 3)
$g4 = $_.BaseName.Substring(12, 3)
$path = "$DataPath\$g1\$g2\$g3\$g4"
if (-not (Test-Path $path)) {
New-Item -ItemType Directory -Path $path
}
Move-Item -Path $_.FullName -Destination $path
}
This code also creates directories in the 3rd $g3 layer for files in "the shorter format", e.g. XX0000AK_G00.txt. This file should however not be moved further than layer $g2. Of course the code above is not capable of doing this, so I tried it with regex below.
This is an alternative idea (not worked out furhter than creating directories), but I failed to continue after
Select-Object -Unique. I am failing to use $Matches[1] in New-Item, because I can only Select-Object -unique the variable $_, not $Matches[1] or even the subdirectory "$($Matches[1])$($Matches[2])". The following code is my attempt.
cd $PSScriptRoot\Test
# Create Folder Layer 1
Get-ChildItem |
% {
$_.BaseName -match "^(\w{2})(\d{4})AN(\d{3})?_(G\d{2})(_\d{3})?$" | Out-Null
$Matches[1]
"$($Matches[1])$($Matches[2])"
} |
Select-Object -Unique |
% {
New-Item -ItemType directory $_
} | Out-Null
I am fairly new to powershell, please don't be too harsh :) I also don't have a programming background, so please excuse the use of incorrect wording.
new-item $dataPath\MM0245AK830_G04_701.txt -ItemType File
new-item $dataPath\VY0245AK_G03.txt -ItemType File
new-item $dataPath\VY0245AK_G03_701.txt -ItemType File
new-item $dataPath\VY0245AK625_G03.txt -ItemType File
new-item $dataPath\VY0245AK625_G03_701.txt -ItemType File
new-item $dataPath\VY0345AK625_G03_701.txt -ItemType File
i am truly bad at complex regex patterns [blush], so this is done with simple string ops, mostly.
what the code does ...
fakes reading in some files
when you have tested this and it works as needed on all your test files, replace the entire #region/#endregion block with a Get-ChildItem call.
iterates thru the collection
splits the BaseName on ak & saves it for later use
checks for a the two short file layouts
checks for 1 _ versus 2
builds the $Dir string for each of those 2 filename layouts
builds the long file name $Dir
uses the previous $Dir stuff to build the $FullDest for each file
shows the various stages for each file
that last section would be replaced with your mkdir & Move-Item commands.
the code ...
#region >>> fake reading in files
# when ready to use the real things, use $Get-ChildItem
$InStuff = #'
MM0245AK625_G03_701.txt
MM0245AK830_G04_701.txt
VY0245AK_G03.txt
VY0245AK_G03_701.txt
VY0245AK625_G03.txt
VY0245AK625_G03_701.txt
VY0345AK625_G03_701.txt
'# -split [System.Environment]::NewLine |
ForEach-Object {
[System.IO.FileInfo]$_
}
#endregion >>> fake reading in files
foreach ($IS_Item in $InStuff)
{
$BNSplit_1 = $IS_Item.BaseName -split 'ak'
if ($BNSplit_1[-1].StartsWith('_'))
{
if (($BNSplit_1[-1] -replace '[^_]').Length -eq 1)
{
$Dir = '{0}\{1}' -f $IS_Item.BaseName.Substring(0, 2),
$IS_Item.BaseName.Substring(0, 6)
}
else
{
$Dir = '{0}\{1}\{2}' -f $IS_Item.BaseName.Substring(0, 2),
$IS_Item.BaseName.Substring(0, 6),
$IS_Item.BaseName.Split('_')[1]
}
}
else
{
$Dir = '{0}\{1}\{2}\{3}' -f $IS_Item.BaseName.Substring(0, 2),
$IS_Item.BaseName.Substring(0, 6),
$BNSplit_1[-1].Split('_')[0],
$BNSplit_1[-1].Split('_')[1]
}
$FullDest = Join-Path -Path $Dir -ChildPath $IS_Item
#region >>> show what was done with each file
# replace this block with your MkDir & Move-Item commands
$IS_Item.Name
$Dir
$FullDest
'depth = {0}' -f ($FullDest.Split('\').Count - 1)
'=' * 20
#endregion >>> show what was done with each file
}
the output ...
MM0245AK625_G03_701.txt
MM\MM0245\625\G03
MM\MM0245\625\G03\MM0245AK625_G03_701.txt
depth = 4
====================
MM0245AK830_G04_701.txt
MM\MM0245\830\G04
MM\MM0245\830\G04\MM0245AK830_G04_701.txt
depth = 4
====================
VY0245AK_G03.txt
VY\VY0245
VY\VY0245\VY0245AK_G03.txt
depth = 2
====================
VY0245AK_G03_701.txt
VY\VY0245\G03
VY\VY0245\G03\VY0245AK_G03_701.txt
depth = 3
====================
VY0245AK625_G03.txt
VY\VY0245\625\G03
VY\VY0245\625\G03\VY0245AK625_G03.txt
depth = 4
====================
VY0245AK625_G03_701.txt
VY\VY0245\625\G03
VY\VY0245\625\G03\VY0245AK625_G03_701.txt
depth = 4
====================
VY0345AK625_G03_701.txt
VY\VY0345\625\G03
VY\VY0345\625\G03\VY0345AK625_G03_701.txt
depth = 4
====================
I would first split each file BaseName on the underscore. Then use a regex to split the first part into several array elements, combine that with a possible second part of the split in order to create the destination folder path for the files.
$DataPath = "$PSScriptRoot\Test"
$files = Get-ChildItem -Path $DataPath -Filter '*_*.txt' -File
foreach ($file in $files) {
$parts = $file.BaseName -split '_'
# regex your way to split the first part into path elements (remove empty items)
$folders = [regex]::Match($parts[0], '(?i)^(.{2})(\d{4})[A-Z]{2}(\d{3})?').Groups[1..3].Value | Where-Object { $_ -match '\S'}
# the second part is a merge with the first part
$folders[1] = $folders[0] + $folders[1]
# if there was a third part after the split on the underscore, add $part[1] (i.e. 'Gxx') to the folders array
if ($parts.Count -gt 2) { $folders += $parts[1] }
# join the array elements with a backslash (i.e. [System.IO.Path]::DirectorySeparatorChar)
# and join all tat to the $DataPath to create the full destination for the file
$target = Join-Path -Path $DataPath -ChildPath ($folders -join '\')
# create the folder if that does not yet exist
$null = New-Item -Path $target -ItemType Directory -Force
# move the file to that (new) directory
$file | Move-Item -Destination $target -WhatIf
}
The -WhatIf switch makes the code not move anything to the new destination, it will only display where the file would go to. Once you are happy with that information, remove -WhatIf and run the code again
After moving your filestructure will look like this:
D:\TEST
+---MM
| \---MM0245
| +---625
| | \---G03
| | MM0245AK625_G03_701.txt
| |
| \---830
| \---G04
| MM0245AK830_G04_701.txt
|
\---VY
+---VY0245
| | VY0245AK_G03.txt
| |
| +---625
| | | VY0245AK625_G03.txt
| | |
| | \---G03
| | VY0245AK625_G03_701.txt
| |
| \---G03
| VY0245AK_G03_701.txt
|
\---VY0345
\---625
\---G03
VY0345AK625_G03_701.txt

Batch rearranging and renaming filenames in PowerShell

I need help with rearranging and renaming a bunch of files in PowerShell.
I want to change from:
YYYY_Project name_City_Category.jpg
to
YYYY_Category_Project name_City.jpg
The years, categories, project names and cities are of course all different.
Please be gentle, I'm new to PowerShell and regex.
Assuming we have an item like this:
get-item '.\YYYY_Project name_City_Category.jpg'
Directory: C:\temp
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 6/8/2020 10:12 AM 8 YYYY_Project name_City_Category.jpg
This is a FileInfo object which has a number of properties, one of which is BaseName which gives us the filename without extension.
PS> $file = get-item '.\YYYY_Project name_City_Category.jpg'
PS> $File.BaseName
YYYY_Project name_City_Category
We can call the .Split() method on the BaseName property to split on every instance of the _ underscore character, like so:
PS> $File.BaseName.Split('_')
YYYY
Project name
City
Category
We can then assign those to variables like this:
$FileSegments = $File.BaseName.Split('_')
$YearPart = $FileSegments[0]
$ProjPart = $FileSegments[1]
$CityPart = $FileSegments[2]
$CatgPart = $FileSegments[3]
We can then reassemble them in our desired order like this:
$newName = $YearPart + "_" + $CatgPart + "_" + $ProjPart + "_" + $CityPart + $file.Extension
write-host $newName
YYYY_Category_Project name_City.jpg
So, you'd put it all together like this. If you like the results, remove -WhatIf
$files = Get-ChildItem C:\PathTo\Your\Directory\*.jpg
ForEach ($file in $files){
$FileSegments = $File.BaseName.Split('_')
$YearPart = $FileSegments[0]
$ProjPart = $FileSegments[1]
$CityPart = $FileSegments[2]
$CatgPart = $FileSegments[3]
$newName = $YearPart + "_" + $CatgPart + "_" + $ProjPart + "_" + $CityPart + $file.Extension
write-host $newName
Rename-Item -Path $file.FullName -NewName $newName -WhatIf
}
You don't need a complex Regex for this, just some understanding of how to get the files in a folder using Get-ChildItem and how Rename-Item works.
Something like this should do it
$sourcePath = 'Folder\To\Where\The\Files\Are'
Get-ChildItem -Path $sourcePath -Filter '*.jpg' -File | Rename-Item -NewName {
# split the file's BaseName into 4 pieces at the underscore
$year, $project, $city, $category = $_.BaseName -split '_', 4
# use the -f Format operator to stitch the parts together in a new order
# see https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_operators?view=powershell-5.1#format-operator--f
'{0}_{1}_{2}_{3}{4}' -f $year, $category, $project, $city, $_.Extension
}
One liner version of the split and join idea:
dir *_*_*_*.jpg | ren -new {((($_.basename -split '_')[0,3,1,2]) -join '_') +'.jpg'} -whatif
What if: Performing the operation "Rename File" on target
"Item: C:\Users\admin\foo\YYYY_Project name_City_Category.jpg
Destination: C:\Users\admin\foo\YYYY_Category_Project name_City.jpg".

Append file name using CSV

I'm trying to rename files that match values in column one of a csv adding the value in column 3 to the beginning of the file name leaving the rest of the file name intact. Here is what I have so far. I cant seem to figure out the Rename-Item.
# Common Paths
$PathRoot = "C:\Temp\somefiles" #Where the files are to rename
# Get csv file
$ClientAccounts = Import-CSV -path "\\server\some\path\to\csv\file.csv"
# Get each file and rename it
ForEach($row in $ClientAccounts)
{
$CurrentClientTaxId = $row[-1].TaxId
$CurrentClientName = $row[-1].ClientName
#loop through files
$FileExists = Test-Path -Path "$PathTotal\*$CurrentClientLB_Number*" #See if there is a file.
If ($FileExists -eq $true) #The file does exist.
{
#ReName File
Rename-Item -Path $PathRoot -NewName {$CurrentClientName + " " + $_.name}
}
}
Lets suppose your CSV file looks similar to this:
"LB_Number","TaxId","ClientName"
"987654","12345","Microsoft"
"321456","91234","Apple"
"741852","81234","HP"
Column 1 has the portion of the existing file name to match
Column 3 has the client name you want to prepend to the file name
Then your function could be something like this:
# Common Paths
$PathRoot = "C:\Temp\somefiles" # Where the files are to rename
# Get csv file
$ClientAccounts = Import-CSV -path "\\server\some\path\to\csv\file.csv"
# Loop through all clients in the CSV
foreach($client in $ClientAccounts) {
$CurrentClientLB_Number = $client.LB_Number
$CurrentClientTaxId = $client.TaxId # unused...??
$CurrentClientName = $client.ClientName
# get the file(s) using wildcards (there can be more than one)
# and rename them
Get-ChildItem -Path "$PathRoot\*$CurrentClientLB_Number*" -File | ForEach-Object {
$_ | Rename-Item -NewName ($CurrentClientName + " " + $_.Name)
}
# Curly braces work also, although this is not very common practice:
# Get-ChildItem -Path "$PathRoot\*$CurrentClientLB_Number*" -File |
# Rename-Item -NewName { ($CurrentClientName + " " + $_.Name) }
}
I use the -File parameter with Get-ChildItem so the function will only return files; not directories. If you are using PowerShell version 2.0, you need to replace that with | Where-Object { !$_.PSIsContainer }.

How can I compare the content of 3 directories in powershell

After looking into this for a while, I've found a lot of people trying to do the same thing but I haven't yet been able to find a complete answer so I'm hoping someone can help us!
What I would like to do is give 2 directories, A and B, and compare everything inside A to everything inside B and if there are any files or folders in B that are not in A then generate an output file detailing the path to the item that is in B and not A. From Here, Id like to use the list of items and say if any of the items in these paths contain something different to the files in 2 directories (the names will be the same so documents\folder\B.txt contains something different to desktop\folder\B.txt) generate another list showing what is different or showing the file path to items that are different if I cant show the text that is different.
I've looked into doing this with .hash but I'm not sure if that's to best way to go about it? something like:
$SourceDocs = Get-ChildItem –Path C:\Documents1 | foreach {Get-FileHash –Path $_.FullName} $DestDocs = Get-ChildItem –Path C:\Documents2 | foreach {Get-FileHash –Path $_.FullName}
In terms of what I have for the physical comparison, I originally was trying to do a 3-way comparison so the code below isn't accurate but its all I've got as yet.
$folderA = 'C:\Users\USERNAME\Desktop\Folder A'
$folderB = 'C:\Users\USERNAME\Desktop\Folder B'
$folderC = 'C:\Users\USERNAME\Desktop\Folder C'
$AChild = Get-ChildItem $folderA -Recurse
$BChild = Get-ChildItem $folderB -Recurse
$CChild = Get-ChildItem $folderC -Recurse
Compare-Object -ReferenceObject $AChild -DifferenceObject $BChild, $CChildcode
After looking into this further, Ive found out it might be better to use 3.0 rather than 2.0 so I can use -Dir more efficiently.
Any questions, let me know.
Any help greatly appreciated.
This is answer to what you want:
What I would like to do is give 2 directories, A and B, and compare everything inside A to everything inside B and if there are any files or folders in B that are not in A then generate an output file detailing the path to the item that is in B and not A.
$folder1 = "C:\Users\USERNAME\Desktop\Folder A"
$folder2 = "C:\Users\USERNAME\Desktop\Folder B"
# Get all files under $folder1, filter out directories
$firstFolder = Get-ChildItem -Recurse $folder1 | Where-Object { -not $_.PsIsContainer }
$failedCount = 0
$i = 0
$totalCount = $firstFolder.Count
$firstFolder | ForEach-Object {
$i = $i + 1
Write-Progress -Activity "Searching Files" -status "Searching File $i of $totalCount" -percentComplete ($i / $firstFolder.Count * 100)
# Check if the file, from $folder1, exists with the same path under $folder2
If ( Test-Path ( $_.FullName.Replace($folder1, $folder2) ) ) {
# Compare the contents of the two files...
If ( Compare-Object (Get-Content $_.FullName) (Get-Content $_.FullName.Replace($folder1, $folder2) ) ) {
# List the paths of the files containing diffs
$fileSuffix = $_.FullName.TrimStart($folder1)
$failedCount = $failedCount + 1
Write-Host "$fileSuffix is on each server, but does not match"
}
}
else
{
$fileSuffix = $_.FullName.TrimStart($folder1)
$failedCount = $failedCount + 1
Write-Host "$fileSuffix is only in folder 1"
$fileSuffix | Out-File "$env:userprofile\desktop\folder1.txt" -Append
}
}
$secondFolder = Get-ChildItem -Recurse $folder2 | Where-Object { -not $_.PsIsContainer }
$i = 0
$totalCount = $secondFolder.Count
$secondFolder | ForEach-Object {
$i = $i + 1
Write-Progress -Activity "Searching for files only on second folder" -status "Searching File $i of $totalCount" -percentComplete ($i / $secondFolder.Count * 100)
# Check if the file, from $folder2, exists with the same path under $folder1
If (!(Test-Path($_.FullName.Replace($folder2, $folder1))))
{
$fileSuffix = $_.FullName.TrimStart($folder2)
$failedCount = $failedCount + 1
Write-Host "$fileSuffix is only in folder 2"
$fileSuffix | Out-File "$env:userprofile\desktop\folder2.txt" -Append
}
}

How to rename files with sequential even and odd numbers in PowerShell?

I would like to know how I can rename the files from a specific folder with a sequence of only even and odds numbers in PowerShell. E.g. Folder1: pag_001.jpg, pag_003.jpg, pag_005.jpg.... pag_201.jpg , Folder2: pag_002.jpg, pag_004.jpg, pag_006.jpg.... pag_200.jpg. It is because I have a document that was scanned first the odds pages and secondly the even pages, therefore their file names are in a consecutive sequence from 1 to 201. Then I separated one half of the files which are the odds pages in a new place: Folder1, and the second half,the even pages in the Folder2. That is why I would like change the names first and the join again together with their new names.
I have tried this based in a similar post:
At the moment I could generate even number sequences like that:
ForEach ($number in 1..100 ) { $number * 2}
and odd numbers like that:
ForEach ($number in 0..100 ) { $number *2+1}
and wanted apply the sequences generated before to rename my files like that:
cd C:\test\Folder1
$i = $number * 2
Get-ChildItem *.jpg | %{Rename-Item $_ -NewName ('pag_{0:D3}.jpg' -f $i++)}
but it doesn't works! Any suggestions are welcome
Regards,
Your $i++ adds 1 each time, this is why it also add even numbers,
You can create array of Odd Numbers then use the $i++ to step one item in the array, like this:
$path = "C:\test\Folder1"
$oddNumbersArray = 0..100 | % {$_ *2 +1}
$i = 0
Get-ChildItem $path -Filter *.jpg | % {Rename-Item $_ -NewName ("pag_$($oddNumbersArray[$i]).jpg") ;$i++}
For Even Numbers change the $oddNumbersArray line to {$_ *2}
Bunch of ways to do this. For mine we add each index as a member so that it is more easily accessible in the rename item script block.
$index = 0
Get-ChildItem $path -Filter "*.jpg" | ForEach-Object{
$index = $index +2
$_ | Add-Member -Name "Index" -MemberType NoteProperty -Value $index -PassThru
} | Rename-Item -NewName {'pag_{0:D3}.jpg' -f $_.Index} -WhatIf
Using Add-Member in a ForEach-Object we update the value of index and then add it as a property of the same name. Then in your rename-item scriptblock we can call that property. Remove the -WhatIf after you verified the new names are what you wanted. Switch $index between 0 and -1 for even and odd respectively.
Another method using a global index variable and mitigating the pipeline by using calculated properties to create the pipeline variables that Rename-Item uses.
$path = "C:\Temp\csv"
$global:index = 0 # Use -1 for odd
Get-ChildItem $path -Filter "*.csv" |
Select-Object #{Name="Path";Expression={$_.FullName}},
#{Name="NewName";Expression={$global:index = $global:index + 2; 'pag_{0:D3}.jpg' -f $global:index}} |
Rename-Item -WhatIf