Exclude Subfolders and Their Parent Folder from a Function - powershell

I have a script that is designed to do three things:
Convert all .webp files to .jpg from the main "Test" and all its differently-titled subfolders.
For each directory, make a sub directory called "Ch1" within.
Move all the files into the "Ch1" folder
The script currently looks like this, and works fine for single-titled folders with only .webp or .jpg files in each:
cd D:\TestingGrounds\Test
get-childItem -recurse | Where {$_.extension -eq ".webp"} | rename-item -newname {$_.name -replace ".webp",".jpg"}
$dirs = Get-ChildItem -force D:\TestingGrounds\Test
foreach ($dir in $dirs) {mkdir D:\TestingGrounds\Test\$dir\ch_1; move D:\TestingGrounds\Test\$dir\* D:\TestingGrounds\Test\$dir\ch_1}
I now have multi-chapter folders that already have subfolders with .webp and .jpg files inside pre-made chapter folders, such as "Ch1", "Ch1.5", "Ch2", etc.
I cannot figure out a way to add an exception or exclusion to these multi-chapter folders where they are not touched by the mkdir and move portions, only all .webp files to still be renamed to .jpg
I'm not very familiar with Powershell, much less exception commands. I've tried -notcontains, Where-Object, and making another designator, like $multi = 'Ch*' to be ignored. So far, nothing has worked. It will continue to make a "Ch1" in the multi-chapter subfolders except for the original "Ch1", and move their respective files into them... basically what the original script did. Attached are photos of what I'm trying to do.
1 Before 2 Desired Outcome
Here are some of my attempts:
$dirs = Get-ChildItem -force 'D:\TestingGrounds\Test';
$Multi = 'Ch*';
get-childItem -recurse | Where {$_.extension -eq ".webp"} | rename-item -newname {$_.name -replace ".webp",".jpg"};
if ($folder -notcontains 'Ch*') {mkdir D:\TestingGrounds\Test\$folder\Ch1; move D:\TestingGrounds\Test\$folder\* D:\TestingGrounds\Test\$folder\Ch1}
{mkdir D:\TestingGrounds\Test\$folder\Ch1; move D:\TestingGrounds\Test\$folder\* D:\TestingGrounds\Test\$folder\Ch1} | Where {D:\Testing Grounds\Test\$dir\ -notcontains 'Ch*'}
gci "D:\TestingGrounds\Test" | Where-Object {$_.FullName -notlike "Ch*"} | mkdir D:\TestingGrounds\Test\$dir\Ch1; move D:\TestingGrounds\Test\$dir\Ch1\* D:\TestingGrounds\Test\$dir\Ch1; move D:\TestingGrounds\Test\$dir\Ch1 D:\TestingGrounds\Test\$dir
$dirs = Get-ChildItem -force D:\TestingGrounds\Test; $Multi = 'Ch*'; get-childItem -recurse | Where {$_.extension -eq ".webp"} | rename-item -newname {$_.name -replace ".webp",".jpg"}; if ($dir -notcontains $Multi) {mkdir D:\Testing Grounds\Test\$dir\Ch1; move D:\Testing Grounds\Test\$dir\* D:\Testing Grounds\Test\$dir\Ch1}

Edited to reflect clarification in the question:
This will get all of the .webp files in a given directory, move them to a Chapter folder (and create it if needed), or just rename them to .jpg if the file is in the right place.
#Get all WEBP files in the source folder
$sourcefolder = "C:\Test"
$files = Get-ChildItem $sourcefolder -Recurse -Filter *.webp
$chapter = "Ch1"
#Loop through each of the files
Foreach($f in $files)
{
$newname = $f.Name -replace ".webp",".jpg"
$directory = $f.DirectoryName
#Case insensitive RegEx to see if the file is already in a chapter folder, rename the file.
if($f.DirectoryName -imatch ".*\\ch\d+")
{
Rename-Item $f.FullName -NewName $newname
}
#Chapter folder NOT exists, and, File is NOT in chapter folder. Otherwise we'll create sub chapter folders
If((!(Test-Path "$directory\$chapter")) -and($f.DirectoryName -inotmatch ".*\\ch\d+"))
{
New-Item -Path "$directory\$chapter" -ItemType Directory
}
#Chapter folder EXISTS and File is NOT in chapter folder. We can now move it where it needs to be
If((Test-Path "$directory\$chapter") -and ($f.DirectoryName -inotmatch ".*\\ch\d+"))
{
Move-Item $f.FullName -Destination "$directory\$chapter\$newname"
}
}

Related

Archive files older than 6 months

Folder X has a lot of subfolders A,B,C,D... each subfolder has a lot of files and I want to archive all files that are in those subfolders and that are older than 6 months. After that check if archive is created and delete the files that have been archived.
Here is what I tried:
#$SourceFolder = "C:\Users\sec\Desktop\X"
ForEach-Object
{
Get-ChildItem -Path "$($_.FullName)" -Exclude "*.zip"
Where-Object {($_.LastWriteTime -lt (Get-Date).AddMonths(-6))} |
Compress-Archive -DestinationPath "$($_.FullName)\06.2020andOlder.zip" -Update;
if (Test-Path 06.2020andOlder.zip) {
Remove-Item -Force
}
}
Assuming you want each subfolder to end up with a .zip archive where the older files are in, try this:
Use Group-Object to group all older files within the same subdirectory together and use that to the create the .zip file and also to remove the original files after zipping.
$SourceFolder = 'D:\Test'
$refDate = (Get-Date).AddMonths(-6).Date # take this from midnight
Get-ChildItem -Path $SourceFolder -File -Recurse -Exclude "*.zip" |
Where-Object { $_.LastWriteTime -lt $refDate } |
Group-Object DirectoryName | ForEach-Object {
# construct the target folder path for the zip file using the Name of each group
$zip = Join-Path -Path $_.Name -ChildPath '06.2020andOlder.zip'
# archive all 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.

Move Files Containing X in their Names to Folder X

I'm Trying to move files containing "Sxx" in their names to folder named "Sxx".
for example:
file1: S01E12.srt ----> /S01/S01E12.srt
file2: S03E14.jpg ----> /S03/S03E14.jpg
etc.
So i came up with these codes for creating folders using files having "Sxx" in their names and then moving them into the right folders.
For creating folders:
foreach ($name in (Get-ChildItem -File | % {$_.BaseName -replace 'E\d{2}',''}))
{
if ($name -like 'S*') {
New-Item -path "$name" -ItemType Directory
}
}
For moving files:
get-childitem -File | where {$_ -like "S01*"} | move-Item -Destination "S01*"
get-childitem -File | where {$_ -like "S02*"} | move-Item -Destination "S02*"
...
etc.
Any idea how to replace hardcoding method for moving part?
If you have any advice or better code for any part that would be awesome too.
Below is my code. I created two foreach loops to go once through all files and afterwards through all folders. I used a substring to get part of the file names.
$files = Get-ChildItem -File -Path C:\users\Lenovo\Desktop\Test
$dir = Get-ChildItem -Path C:\users\lenovo\Desktop\Test -Directory
foreach ($item in $files) {
foreach ($folder in $dir)
{
if (($item.Name).Substring(0,3) -like $folder.Name)
{
Move-Item -Path $item.FullName -Destination $folder.FullName
}
}
}
I create four files with S01.txt to S04.txt and it was working. To test whether your variable is empty, either you can write the variable with Write-Host or slightly more advanced, you can use the debug mode to see exactly what your variables contain.

Cycle through sub-folders to rename files in Powershell

I have a file directory that contains many folders within it. Inside each of these sub-folders, I have a variety of files. I would like to go through each file, rename some of the items, and add extensions to some of them. I am using Powershell to do this.
I have file names with "." that all need to be replaced with "_" for example, "wrfprs_d02.03" should be "wrfprs_d02_03". I was able to successfully do that in one folder with the following code:
dir | rename-item -NewName {$_.name -replace "wrfprs_d02.","wrfprs_d02_"}
After, I make those replacements, I want to add .grb extensions on to some of the files, which all happen to start with "w", and I was able to do that within one folder with:
Get-ChildItem | Where-Object {$_.Name -match "^[w]"} | ren -new {$_.name + ".grb"}
When I step back from one folder and try to do it iteratively within many folders, my code doesn't work. I am in a directory called "Z:\Windows.Documents\My Documents\Test_data\extracted" which contains all my sub-folders that I want to iterate over. I am using the following code:
$fileDirectory = "Z:\Windows.Documents\My Documents\Test_data\extracted"
foreach($file in Get-ChildItem $fileDirectory)
{
dir | rename-item -NewName {$_.name -replace "wrfprs_d02.","wrfprs_d02_"}
Get-ChildItem | Where-Object {$_.Name -match "^[w]"} | ren -new {$_.name + ".grb"}
}
Any ideas on what my problem is?
because you $_ is replaced into loop when you use pipe. I propose you a new code:
$fileDirectory = "Z:\Windows.Documents\My Documents\Test_data\extracted"
Get-ChildItem $fileDirectory -recurse -file -filter "*.*" |
%{
#replace . by _
$NewName=$_.Name.Replace(".", "_")
#add extension grb if name start by w
if ($NewName -like "w*") {$NewName="$NewName.grb"}
#Add path file
$NewName=Join-Path -Path $_.directory -ChildPath $NewName
#$NewName
#rename
Rename-Item $_.FullName $NewName
}
Not sure what error you were getting, but using rename-item can be finicky. Or at least so in my experience.
I used the follow without issue. My files names were different so I replaced all periods with underscores. If the file starts with "W" then it changed the extension for that file.
$FilePath = Get-ChildItem "Z:\Windows.Documents\My Documents\Test_data\extracted" -Recurse -File
foreach ($file in $FilePath)
{
$newName = $file.Basename.replace(".","_")
$New = $newName + $file.Extension
if($file.Name -match "^[w]")
{
Rename-Item $file.FullName -NewName "$($New).grb"
}
else
{
Rename-Item $file.FullName -NewName $New
}
}
Hope that helps.

Delete Folders where count>5

I Need some help here: the following script needs to be changed so, that the script delete only Folders in the Subdirectory not Files. Can anyone help me?
$path = "C:\test\1"
$keep = 3
$strLogFileName = "c:\test\yourlogfile.log";
function Log-Message
{
Param ([string]$logtext)
Add-content $strLogFileName -value $logtext
}
$dirs = Get-ChildItem -Path $path -Recurse | Where-Object {$_.PsIsContainer}
foreach ($dir in $dirs) {
$files = Get-ChildItem -Path $dir.FullName | Where-Object {-not $_.PsIsContainer -and $_.name - like "*.zip"}
if ($files.Count -gt $keep) {
$files | Sort-Object CreationTime -desc| Select-Object -First ($files.Count - $keep) |
% { $dt=get-date;(Log-Message "Deleting File $_ on $dt");$_ }| Remove-Item -Force
}
}
My original answer was -WAY- off base, and having RTFQ I've got something that should work for you.
function Remove-LargeFolders
{
Param([string]$RootPath)
$keep = 5
#Get a list of the dirs in the first level of the folder
$dirs = Get-ChildItem -Path $RootPath | Where-Object {$_.PsIsContainer}
foreach ($dir in $dirs) {
#Call function on the new folder to check all the sublevels before deleting
#the top-level folder.
Remove-LargeFolders $dir.FullName
$files = Get-ChildItem -Path $dir.FullName | Where-Object {-not $_.PsIsContainer `
-and $_.name -like "*.zip"}
if ($files.Count -gt $keep) {
$dt=get-date
Log-Message "Deleting Folder $dir on $dt"
$dir
Remove-Item $dir.FullName -Force -Recurse
}
}
}
This should do what you're looking for. The answer was recursion! Essentially, the script only looks at one level of folders at a time. It digs down to the bottom of each folder tree, before working it's way back up to the top.
This script will work as written, but there is a proviso. Right now, if the script is targeting a structure like this:
Target Folder (4 files)
SubFolder (10 files)
SubSubFolder (5 files)
It will check SubSubFolder first and not delete it, since it has 5 or fewer files. However, once it hops back up to SubFolder, it will see a folder that is too big and kill it off, getting rid of SubSubFolder in the process. If you want a way around that, you'll need to build in some checks that will allow you to see if each folder has another folder in it before deleting.
Hope this helps more!

Rename file using Powershell and create subfolders

I have 10K documents in a directory with this type of naming convention:
1050_14447_Letter Extension.pdf, 1333_14444_Letter.docx, etc...
I tried using this script to remove all characters before the 2nd underscore (including the 2nd underscore):
Get-ChildItem -Filter *.pdf | Rename-Item -NewName {$_.Name -replace '^[0-9_]+'}
This worked, but revealed there would be duplicate file names.
Wondering if there is some way a script can create a subfolder based on the filename (minus the extension)? So there would be 10K subfolders in my main folder. Each subfolder would just have the one file.
This should work. It creates a new folder for each item, then moves it, renaming it in the process.
gci | ? {!$_.PSIsContainer} | % {New-Item ".\$($_.BaseName)" -Type Directory; Move-Item $_ ".\$($_.BaseName)\$($_.Name -replace '^[0-9_]+')"}
Note that if there are two files with the same name, but different extensions, you'll see an error when trying to create the directory, but both files will wind up in the same folder.
Alternately, if you want something more readable to save in a script, this is functionally identical:
$files = Get-ChildItem | Where-Object {!$_.PSIsContainer}
foreach ($file in $files)
{
$pathName = ".\" + $file.BaseName
New-Item $pathName -Type Directory
$newFileName = $pathName + "\" + ($file.Name -replace '^[0-9_]+')
Move-Item $file $newFileName
}