Skip to content

Instantly share code, notes, and snippets.

@jason-riddle
Last active March 3, 2025 18:02
Show Gist options
  • Save jason-riddle/6bcd4e30a40c51779931294573a03172 to your computer and use it in GitHub Desktop.
Save jason-riddle/6bcd4e30a40c51779931294573a03172 to your computer and use it in GitHub Desktop.
<#
1.ps1
This script groups photo files from a root directory into DVD-sized groups.
It processes files in alphabetical order (excluding hidden files, symlinks,
files with .json, .xmp, .csv, or .ps1 extensions) and writes a CSV file ("groups.csv")
listing each file's group, group size, full path, and file size in bytes.
Usage:
.\1.ps1 [-Mode test]
If the parameter -Mode is set to "test", the DVD capacity is set to 200 MB.
Otherwise, a production capacity of 4.7 GB (binary) is used.
#>
param(
[string]$Mode = ""
)
# Logging function using ISO 8601 UTC timestamps
function Log {
param([string]$Message)
$timestamp = (Get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ")
$logMessage = "$timestamp $Message"
# Output to console
Write-Host $logMessage
# Output to log file
Add-Content -Path ".\1.log" -Value $logMessage
}
# Configuration
$ROOT_DIR = "E:\Photos"
$CSV_FILE = "1.csv"
# Create or clear the log file
if (Test-Path ".\1.log") {
Clear-Content ".\1.log"
} else {
New-Item -Path ".\1.log" -ItemType File -Force | Out-Null
}
Log "Script started"
# Set DVD capacity
if ($Mode -eq "test") {
$DVD_CAPACITY = 200 * 1024 * 1024
Log "Running in test mode with DVD capacity of 200 MB"
} else {
# 4.7 GB in binary (approximately 5046586572 bytes)
$DVD_CAPACITY = 5046586572
Log "Running in production mode with DVD capacity of 4.7 GB"
}
# Initialize CSV file with header (overwrite if exists)
"Group,Group Size,File,File Size" | Out-File -FilePath $CSV_FILE -Encoding utf8
Log "Created CSV file: $CSV_FILE"
$groupNumber = 1
$groupSize = 0
$currentGroupRows = New-Object System.Collections.Generic.List[string]
$oversizedFiles = New-Object System.Collections.Generic.List[object]
Log "Scanning for files in $ROOT_DIR"
# Retrieve files from ROOT_DIR:
# - Exclude hidden files (names starting with a dot)
# - Exclude symbolic links (filter out files with the ReparsePoint attribute)
# - Exclude files with .json, .xmp, .csv, or .ps1 extensions (case-insensitive)
$files = Get-ChildItem -Path $ROOT_DIR -Recurse -File | Where-Object {
# Exclude hidden files (names starting with a dot)
-not ($_.Name.StartsWith('.')) -and
# Exclude symbolic links (ReparsePoint attribute)
-not ($_.Attributes -band [System.IO.FileAttributes]::ReparsePoint) -and
# Exclude .json, .xmp, .csv, and .ps1 files (case-insensitive)
($_.Extension.ToLower() -notin @('.json', '.xmp', '.csv', '.ps1'))
} | Sort-Object -Property FullName
Log "Found $($files.Count) files to process"
foreach ($file in $files) {
$size = $file.Length
# Check if the file is larger than the DVD capacity
if ($size -gt $DVD_CAPACITY) {
$oversizedFiles.Add($file)
$sizeGB = [math]::Round($size / 1073741824, 2)
Log "Oversized file (skipped for grouping): $($file.FullName) (size: ${sizeGB}GB)"
continue
}
# If adding this file exceeds the DVD capacity, flush the current group.
if (($groupSize + $size) -gt $DVD_CAPACITY) {
if ($currentGroupRows.Count -gt 0) {
$groupSizeGB = [math]::Round($groupSize / 1073741824, 2)
foreach ($row in $currentGroupRows) {
"$groupNumber,$groupSizeGB GB,$row" | Add-Content -Path $CSV_FILE
}
# Insert 15 empty rows after the group
1..15 | ForEach-Object { "" | Add-Content -Path $CSV_FILE }
Log "Finished Group $groupNumber with total size $groupSizeGB GB"
$groupNumber++
}
# Reset for new group
$groupSize = 0
$currentGroupRows.Clear()
}
# Prepare CSV row entry for the file (file path in quotes, then size)
$escapedPath = '"' + $file.FullName + '"'
$currentGroupRows.Add("$escapedPath,$size")
$groupSize += $size
}
# Flush the final group if any rows remain
if ($currentGroupRows.Count -gt 0) {
$groupSizeGB = [math]::Round($groupSize / 1073741824, 2)
foreach ($row in $currentGroupRows) {
"$groupNumber,$groupSizeGB GB,$row" | Add-Content -Path $CSV_FILE
}
1..15 | ForEach-Object { "" | Add-Content -Path $CSV_FILE }
Log "Finished Group $groupNumber with total size $groupSizeGB GB"
}
# Log any oversized files
if ($oversizedFiles.Count -gt 0) {
Log "The following files are oversized and do not fit on a DVD:"
foreach ($of in $oversizedFiles) {
$size = $of.Length
$sizeGB = [math]::Round($size / 1073741824, 2)
Log "Oversized file: $($of.FullName) (size: ${sizeGB}GB)"
}
}
Log "DVD grouping complete. CSV output saved to $CSV_FILE."
Log "Script completed"
# CSV Transformation Script
#
# DESCRIPTION:
# This script transforms a CSV file containing file information by adding a "Burn Path" column.
# The burn path is constructed using a base path, group number, and the original filename.
#
# PARAMETERS:
# -InputCSV : Path to the input CSV file (required)
# -OutputCSV : Path where the transformed CSV will be saved (required)
# -BurnBasePath : Base directory for the burn paths (optional, default is "E:\Burn")
# -LogFile : Path to the log file (optional, default is "E:\Photos\2.txt")
#
# EXAMPLE USAGE:
# # Using all defaults:
# .\2.ps1
#
# # Specifying input and output:
# .\2.ps1 -InputCSV C:\Data\input.csv -OutputCSV C:\Data\output.csv
#
# # Using custom burn base path:
# .\2.ps1 -InputCSV C:\Data\input.csv -OutputCSV C:\Data\output.csv -BurnBasePath "D:\MyBackups"
#
# # Specifying a custom log file:
# .\2.ps1 -LogFile "C:\Logs\csv_transform.log"
#
# INPUT CSV FORMAT:
# The input CSV should have columns: Group, Group Size, File, File Size
#
# OUTPUT CSV FORMAT:
# The output CSV will have columns: Group, Group Size, File, File Size, Burn Path
# Where Burn Path follows the format: [BurnBasePath]\Group [Group Number]\[Filename]
#
# NOTES:
# - The script extracts filenames from Windows-style paths
# - Make sure all paths are accessible with current permissions
# - All operations are logged with timestamps to the specified log file
param (
[Parameter(Mandatory=$false)]
[string]$InputCSV = "E:\Photos\1.csv",
[Parameter(Mandatory=$false)]
[string]$OutputCSV = "E:\Photos\2.csv",
[Parameter(Mandatory=$false)]
[string]$BurnBasePath = "E:\Burn",
[Parameter(Mandatory=$false)]
[string]$LogFile = "E:\Photos\2.txt"
)
# Function to write log entries with timestamps
function Write-Log {
param (
[Parameter(Mandatory=$true)]
[string]$Message,
[Parameter(Mandatory=$false)]
[ValidateSet("INFO", "WARNING", "ERROR")]
[string]$Level = "INFO"
)
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$logEntry = "[$timestamp] [$Level] $Message"
# Write to log file
Add-Content -Path $LogFile -Value $logEntry
# Also output to console with appropriate coloring
switch ($Level) {
"INFO" { Write-Host $logEntry }
"WARNING" { Write-Host $logEntry -ForegroundColor Yellow }
"ERROR" { Write-Host $logEntry -ForegroundColor Red }
}
}
# Start logging
Write-Log "Script execution started"
Write-Log "Parameters: InputCSV=$InputCSV, OutputCSV=$OutputCSV, BurnBasePath=$BurnBasePath"
# Check if input file exists
if (-not (Test-Path $InputCSV)) {
Write-Log "Input file not found: $InputCSV" -Level "ERROR"
exit 1
}
Write-Log "Input file verified: $InputCSV"
try {
# Import the CSV
Write-Log "Importing CSV from $InputCSV"
$data = Import-Csv -Path $InputCSV
Write-Log "Successfully imported CSV with $($data.Count) rows"
# Create a new array to hold the transformed data
$outputData = @()
Write-Log "Beginning data transformation"
# Process each row
$processedCount = 0
foreach ($row in $data) {
# Extract the filename from the full path
$filename = Split-Path $row.File -Leaf
# Create a new object with all original properties plus the burn path
$newRow = [PSCustomObject]@{
'Group' = $row.Group
'Group Size' = $row.'Group Size'
'File' = $row.File
'File Size' = $row.'File Size'
'Burn Path' = "$BurnBasePath\Groups\Group $($row.Group)\$filename"
}
# Add the new row to our output data
$outputData += $newRow
$processedCount++
# Log progress periodically (every 1000 rows)
if ($processedCount % 1000 -eq 0) {
Write-Log "Processed $processedCount rows"
}
}
Write-Log "Completed processing all $processedCount rows"
# Export to CSV
Write-Log "Exporting transformed data to $OutputCSV"
$outputData | Export-Csv -Path $OutputCSV -NoTypeInformation
Write-Log "Successfully exported data to CSV"
Write-Log "Transformation complete. Output saved to $OutputCSV"
}
catch {
Write-Log "An error occurred during processing: $_" -Level "ERROR"
Write-Log "Stack trace: $($_.ScriptStackTrace)" -Level "ERROR"
exit 1
}
finally {
Write-Log "Script execution completed"
}
# I've modified the script to add a numbered suffix to files instead of overwriting them
# This change is specifically focused on the file copying logic
param (
[string]$CsvPath = "E:\Photos\2.csv",
[int]$ResumeFromGroup = 0,
[switch]$NoCountdown,
[string]$LogPath = ".\3.log"
)
# Initialize counters and log array
$filesCopied = 0
$errorCount = 0
$skippedCount = 0
$logEntries = @()
$progressFilePath = ".\FileCopyProgress.json"
$startTime = Get-Date
function Write-Log {
param (
[string]$Message,
[string]$Type = "INFO",
[switch]$NoConsole
)
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss.fff"
$logEntry = "[$timestamp] [$Type] $Message"
# Add to log array
$script:logEntries += $logEntry
# Write to console with appropriate color unless NoConsole is specified
if (-not $NoConsole) {
switch ($Type) {
"ERROR" { Write-Host $logEntry -ForegroundColor Red }
"WARNING" { Write-Host $logEntry -ForegroundColor Yellow }
"SUCCESS" { Write-Host $logEntry -ForegroundColor Green }
"PROGRESS" { Write-Host $logEntry -ForegroundColor Cyan }
"RESUME" { Write-Host $logEntry -ForegroundColor Magenta }
"DEBUG" { Write-Host $logEntry -ForegroundColor Gray }
default { Write-Host $logEntry }
}
}
# Always write to log file
Add-Content -Path $LogPath -Value $logEntry
}
function Exit-WithError {
param (
[string]$ErrorMessage
)
Write-Log $ErrorMessage "ERROR"
Write-Log "Script execution aborted due to error." "ERROR"
Write-Summary
exit 1
}
function Write-Summary {
# Calculate execution time
$endTime = Get-Date
$executionTime = $endTime - $startTime
$formattedTime = "{0:hh\:mm\:ss\.fff}" -f $executionTime
$summaryText = @"
====== SUMMARY ======
Start time: $(Get-Date $startTime -Format "yyyy-MM-dd HH:mm:ss")
End time: $(Get-Date $endTime -Format "yyyy-MM-dd HH:mm:ss")
Execution time: $formattedTime
Files processed: $($filesCopied + $errorCount + $skippedCount)
Files copied: $filesCopied
Files skipped: $skippedCount
Errors: $errorCount
Log file: $LogPath
Progress file: $progressFilePath
"@
# Add resume information if applicable
if ($currentGroup -gt 0) {
$summaryText += "`nTo resume, use: $($MyInvocation.MyCommand.Name) -ResumeFromGroup $($currentGroup + 1)"
}
$summaryText += "`n======================"
# Log the summary
Write-Log $summaryText "SUMMARY"
# Display color-coded summary in console
Write-Host "`n====== SUMMARY ======" -ForegroundColor Cyan
Write-Host "Start time: $(Get-Date $startTime -Format "yyyy-MM-dd HH:mm:ss")"
Write-Host "End time: $(Get-Date $endTime -Format "yyyy-MM-dd HH:mm:ss")"
Write-Host "Execution time: $formattedTime"
Write-Host "Files processed: $($filesCopied + $errorCount + $skippedCount)"
Write-Host "Files copied: $filesCopied" -ForegroundColor Green
Write-Host "Files skipped: $skippedCount" -ForegroundColor Yellow
if ($errorCount -gt 0) {
Write-Host "Errors: $errorCount" -ForegroundColor Red
}
else {
Write-Host "Errors: 0" -ForegroundColor Green
}
Write-Host "Log file: $LogPath"
Write-Host "Progress file: $progressFilePath"
# Show resume information
if ($currentGroup -gt 0) {
Write-Host "`nTo resume, use: $($MyInvocation.MyCommand.Name) -ResumeFromGroup $($currentGroup + 1)" -ForegroundColor Yellow
}
Write-Host "======================" -ForegroundColor Cyan
}
function Save-Progress {
param (
[int]$GroupNumber,
[string]$LastFilePath,
[string]$Status = "In Progress"
)
$currentTime = Get-Date
$elapsedTime = $currentTime - $startTime
$formattedElapsedTime = "{0:hh\:mm\:ss\.fff}" -f $elapsedTime
$progressData = @{
LastGroup = $GroupNumber
LastFile = $LastFilePath
Timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss.fff"
Status = $Status
FilesCopied = $filesCopied
ErrorCount = $errorCount
SkippedCount = $skippedCount
ElapsedTime = $formattedElapsedTime
CsvPath = $CsvPath
}
# Calculate copy rate (files per minute)
if ($elapsedTime.TotalMinutes -gt 0) {
$filesPerMinute = [math]::Round($filesCopied / $elapsedTime.TotalMinutes, 2)
$progressData.Add("FilesPerMinute", $filesPerMinute)
}
# Save to JSON file
$progressData | ConvertTo-Json | Set-Content -Path $progressFilePath
# Log progress update (use NoConsole to avoid excessive console output)
$logMsg = "Progress saved: Group $GroupNumber, File: $LastFilePath, Status: $Status"
if ($Status -eq "In Progress") {
Write-Log $logMsg "PROGRESS" -NoConsole
} else {
Write-Log $logMsg "PROGRESS"
}
}
function Show-Countdown {
param (
[int]$Seconds = 30
)
Write-Host "`nStarting file copy in $Seconds seconds..." -ForegroundColor Yellow
Write-Host "Press Ctrl+C to cancel" -ForegroundColor Yellow
for ($i = $Seconds; $i -gt 0; $i--) {
Write-Host "`rCountdown: $i seconds remaining..." -NoNewline -ForegroundColor Cyan
Start-Sleep -Seconds 1
}
Write-Host "`rCountdown complete. Starting copy process... " -ForegroundColor Green
Write-Host ""
}
# Function to generate a unique filename by adding a numbered suffix
function Get-UniqueFilename {
param (
[string]$FilePath
)
if (-not (Test-Path -Path $FilePath)) {
return $FilePath
}
$directory = Split-Path -Path $FilePath -Parent
$filename = Split-Path -Path $FilePath -Leaf
$filenameWithoutExt = [System.IO.Path]::GetFileNameWithoutExtension($filename)
$extension = [System.IO.Path]::GetExtension($filename)
$counter = 1
$newFilePath = $FilePath
while (Test-Path -Path $newFilePath) {
$newFilename = "{0}-{1}{2}" -f $filenameWithoutExt, $counter, $extension
$newFilePath = Join-Path -Path $directory -ChildPath $newFilename
$counter++
}
return $newFilePath
}
# Create or clear log file
$logHeader = @"
=================================================================
=== File Copy Log - $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') ===
=================================================================
Script: $($MyInvocation.MyCommand.Name)
CSV Path: $CsvPath
Resume Group: $ResumeFromGroup
Countdown: $(-not $NoCountdown)
Log Path: $LogPath
Computer: $env:COMPUTERNAME
User: $env:USERNAME
PowerShell: $($PSVersionTable.PSVersion)
=================================================================
"@
Set-Content -Path $LogPath -Value $logHeader
# Display resume information if applicable
if ($ResumeFromGroup -gt 0) {
Write-Log "Resuming copy process from Group $ResumeFromGroup" "RESUME"
}
else {
Write-Log "Starting new copy process" "INFO"
}
# Show countdown unless skipped
if (-not $NoCountdown) {
Show-Countdown -Seconds 30
}
# Validate CSV file exists
if (-not (Test-Path $CsvPath)) {
Exit-WithError "CSV file not found at path: $CsvPath"
}
Write-Log "Starting file copy process using CSV: $CsvPath"
try {
# Import the CSV file
$csvData = Import-Csv -Path $CsvPath
# Validate CSV structure
if ($csvData.Count -eq 0) {
Exit-WithError "CSV file contains no data rows."
}
# Check for required columns
$requiredColumns = @("Group", "File", "Burn Path")
foreach ($column in $requiredColumns) {
if (-not $csvData[0].PSObject.Properties.Name.Contains($column)) {
Exit-WithError "CSV file is missing required column: $column"
}
}
# Count total files to process (after resume point)
$totalFiles = ($csvData | Where-Object { [int]$_.Group -ge $ResumeFromGroup }).Count
$totalGroups = ($csvData | Where-Object { [int]$_.Group -ge $ResumeFromGroup } | Select-Object -Property Group -Unique).Count
Write-Log "CSV validation successful. Found $totalFiles files across $totalGroups groups to process."
Write-Log "Starting Group: $ResumeFromGroup, Ending Group: $(($csvData | Sort-Object {[int]$_.Group} -Descending | Select-Object -First 1).Group)"
# Log CSV structure details
$groupSizes = $csvData | Group-Object -Property Group | ForEach-Object {
[PSCustomObject]@{
Group = $_.Name
FileCount = $_.Count
}
}
Write-Log "CSV Group Structure:`n$(($groupSizes | Format-Table -AutoSize | Out-String).Trim())" "DEBUG"
# Track current group for progress saving
$currentGroup = 0
# Process each row in the CSV
foreach ($row in $csvData) {
# Parse group number (handle string or integer)
$groupNumber = [int]$row.Group
$currentGroup = $groupNumber
# Skip groups before resume point
if ($groupNumber -lt $ResumeFromGroup) {
continue
}
$sourceFile = $row.File
$destinationFile = $row."Burn Path"
# Validate source file exists
if (-not (Test-Path $sourceFile)) {
Write-Log "Source file not found: $sourceFile" "ERROR"
Write-Log "File details: Group $groupNumber, Source: $sourceFile, Destination: $destinationFile" "DEBUG"
$errorCount++
continue # Continue with next file instead of aborting
}
# Log file details and size
try {
$fileInfo = Get-Item -Path $sourceFile
$fileSizeMB = [math]::Round($fileInfo.Length / 1MB, 2)
Write-Log "File details: Group $groupNumber, Size: $fileSizeMB MB, Last Modified: $($fileInfo.LastWriteTime)" "DEBUG" -NoConsole
} catch {
Write-Log "Could not get file details for $sourceFile = $($_.Exception.Message)" "WARNING" -NoConsole
}
# Create destination directory if it doesn't exist
$destinationDir = Split-Path -Path $destinationFile -Parent
if (-not (Test-Path $destinationDir)) {
Write-Log "Creating destination directory: $destinationDir"
try {
New-Item -Path $destinationDir -ItemType Directory -Force | Out-Null
}
catch {
Write-Log "Failed to create destination directory: $destinationDir. Error: $($_.Exception.Message)" "ERROR"
$errorCount++
continue # Continue with next file instead of aborting
}
}
# Copy the file
$copyStartTime = Get-Date
try {
# Check if destination already exists and compare sizes
$destinationExists = Test-Path -Path $destinationFile
if ($destinationExists) {
$destFileInfo = Get-Item -Path $destinationFile
$sourceFileInfo = Get-Item -Path $sourceFile
# If files are identical (same size and last write time within 2 seconds)
if (($destFileInfo.Length -eq $sourceFileInfo.Length) -and
([math]::Abs(($destFileInfo.LastWriteTime - $sourceFileInfo.LastWriteTime).TotalSeconds) -lt 2)) {
Write-Log "File already exists and appears identical. Skipping: $destinationFile" "WARNING"
$skippedCount++
Save-Progress -GroupNumber $groupNumber -LastFilePath $destinationFile -Status "Skipped"
continue
} else {
# Generate a unique filename instead of overwriting
$originalDestination = $destinationFile
$destinationFile = Get-UniqueFilename -FilePath $destinationFile
Write-Log "Destination file exists but differs from source. Creating file with suffix: $destinationFile" "WARNING"
}
}
# Log the copy operation with the possibly modified destination path
Write-Log "Copying file (Group $groupNumber): $sourceFile -> $destinationFile"
# Perform the copy operation
Copy-Item -Path $sourceFile -Destination $destinationFile -Force
$filesCopied++
# Calculate and log copy performance
$copyEndTime = Get-Date
$copyDuration = $copyEndTime - $copyStartTime
$fileSize = (Get-Item -Path $destinationFile).Length
$speedMBps = if ($copyDuration.TotalSeconds -gt 0) {
[math]::Round(($fileSize / 1MB) / $copyDuration.TotalSeconds, 2)
} else {
"N/A"
}
Write-Log "Copy completed in $($copyDuration.TotalSeconds.ToString("0.00")) seconds ($speedMBps MB/s)" "SUCCESS"
# Verify copy was successful (compare file sizes)
$sourceSize = (Get-Item -Path $sourceFile).Length
$destSize = (Get-Item -Path $destinationFile).Length
if ($sourceSize -eq $destSize) {
Write-Log "Verification successful: Source and destination files match ($($sourceSize / 1MB) MB)" "SUCCESS" -NoConsole
} else {
Write-Log "Verification WARNING: Source ($($sourceSize / 1MB) MB) and destination ($($destSize / 1MB) MB) file sizes differ" "WARNING"
}
# Save progress after each successful copy
Save-Progress -GroupNumber $groupNumber -LastFilePath $destinationFile
}
catch {
Write-Log "Failed to copy file: $sourceFile -> $destinationFile. Error: $($_.Exception.Message)" "ERROR"
Write-Log "Exception details: $($_.Exception | Format-List -Force | Out-String)" "DEBUG"
$errorCount++
Save-Progress -GroupNumber $groupNumber -LastFilePath $destinationFile -Status "Error"
continue # Continue with next file instead of aborting
}
# Display progress with detailed information
$currentProgress = $filesCopied + $errorCount + $skippedCount
$percentComplete = [math]::Round(($currentProgress / $totalFiles) * 100, 2)
# Calculate estimated time remaining
$elapsedTime = (Get-Date) - $startTime
if ($currentProgress -gt 0 -and $elapsedTime.TotalSeconds -gt 0) {
$filesPerSecond = $currentProgress / $elapsedTime.TotalSeconds
$remainingFiles = $totalFiles - $currentProgress
$estimatedSecondsRemaining = if ($filesPerSecond -gt 0) { $remainingFiles / $filesPerSecond } else { 0 }
$estimatedTimeRemaining = [TimeSpan]::FromSeconds($estimatedSecondsRemaining)
$estimatedCompletion = (Get-Date).AddSeconds($estimatedSecondsRemaining)
$statusMsg = "Group: $groupNumber | Progress: $percentComplete% | ETA: $($estimatedCompletion.ToString("HH:mm:ss"))"
# Log progress every 10 files or every 5% change
if (($currentProgress % 10 -eq 0) -or ($percentComplete % 5 -eq 0 -and $percentComplete -gt 0)) {
Write-Log "Progress: $currentProgress/$totalFiles files ($percentComplete%) | Copied: $filesCopied | Errors: $errorCount | Skipped: $skippedCount | ETA: $($estimatedCompletion.ToString("HH:mm:ss"))" "PROGRESS"
}
} else {
$statusMsg = "Group: $groupNumber | Progress: $percentComplete%"
}
# Write-Progress -Activity "Copying Files" -Status $statusMsg -PercentComplete $percentComplete -CurrentOperation "Copying: $sourceFile"
}
Write-Progress -Activity "Copying Files" -Completed
# Generate final statistics
$endTime = Get-Date
$totalDuration = $endTime - $startTime
$formattedDuration = "{0:hh\:mm\:ss\.fff}" -f $totalDuration
$totalFilesCopied = $filesCopied + $skippedCount
$successRate = if ($totalFiles -gt 0) { [math]::Round(($totalFilesCopied / $totalFiles) * 100, 2) } else { 0 }
Write-Log "File copy process completed. Duration: $formattedDuration, Success rate: $successRate%" "SUCCESS"
Write-Log "Copied: $filesCopied, Skipped: $skippedCount, Errors: $errorCount" "SUCCESS"
# Save final progress with completion status
Save-Progress -GroupNumber $currentGroup -LastFilePath "Last file processed" -Status "Completed"
# Generate and display summary
Write-Summary
}
catch {
$errorCount++
Exit-WithError "An unexpected error occurred: $($_.Exception.Message)"
}
finally {
# Always save final progress
Save-Progress -GroupNumber $currentGroup -LastFilePath "Script completed or interrupted"
}
<#
.SYNOPSIS
Automates burning multiple folders to DVDs and moves processed folders to a destination.
.DESCRIPTION
This script automates the process of burning folders within a source directory to DVDs,
one folder per DVD. After successful burning, the script moves each processed folder to
a specified destination. It handles user prompts for disc insertion, error handling,
and provides progress feedback.
.PARAMETER SourceDirectory
Path to the directory containing folders to burn. Each subfolder will be burned to a separate DVD.
Default is "E:\Burn\Groups"
.PARAMETER ProcessedFolder
Destination folder where processed folders will be moved after burning.
Default is "E:\BurnedGroups\Groups"
.PARAMETER DVDDriveLetter
The DVD drive letter to use for burning.
Default is "D:"
.EXAMPLE
.\4.ps1
.EXAMPLE
.\4.ps1 -SourceDirectory "E:\Burn\Groups"
.EXAMPLE
.\4.ps1 -SourceDirectory "E:\Burn\Groups" -ProcessedFolder "E:\BurnedGroups\Groups" -DVDDriveLetter "D:"
.NOTES
Author: DVD Burning Script
Version: 1.2
Requires: Windows PowerShell 5.1 or higher
#>
param (
[Parameter(Mandatory=$false, HelpMessage="Path to the directory containing folders to burn")]
[string]$SourceDirectory = "E:\Burn\Groups",
[Parameter(Mandatory=$false, HelpMessage="Destination folder where processed folders will be moved")]
[string]$ProcessedFolder = "E:\BurnedGroups\Groups",
[Parameter(Mandatory=$false, HelpMessage="DVD drive letter to use")]
[string]$DVDDriveLetter = "D:"
)
# Set up logging file
$LogFile = "4.log"
# Function to write log messages to both console and log file
function Write-Log {
param (
[Parameter(Mandatory=$true)]
[string]$Message,
[Parameter(Mandatory=$false)]
[string]$ForegroundColor = "White"
)
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$logMessage = "[$timestamp] $Message"
# Write to console with color
Write-Host $logMessage -ForegroundColor $ForegroundColor
# Write to log file
Add-Content -Path $LogFile -Value $logMessage
}
function Burn-DVD {
param (
[string]$folderPath,
[string]$label = "Data DVD",
[string]$driveLetter
)
try {
Write-Log "Starting DVD burning process for folder: $folderPath" -ForegroundColor Cyan
# Create COM objects
$burn = New-Object -ComObject IMAPI2.MsftDiscMaster2
$recorder = New-Object -ComObject IMAPI2.MsftDiscRecorder2
$discFormat = New-Object -ComObject IMAPI2.MsftDiscFormat2Data
# Check if a recorder is available
if ($burn.Count -eq 0) {
Write-Log "No DVD burner found." -ForegroundColor Red
return $false
}
# Find the specific recorder by drive letter
$found = $false
for ($i = 0; $i -lt $burn.Count; $i++) {
$recorderID = $burn.Item($i)
$recorder.InitializeDiscRecorder($recorderID)
# Get the volume paths for this recorder
$volumePaths = $recorder.VolumePathNames
foreach ($path in $volumePaths) {
if ($path -eq $driveLetter) {
$found = $true
Write-Log "Found DVD drive at $driveLetter" -ForegroundColor Green
break
}
}
if ($found) {
break
}
}
if (-not $found) {
Write-Log "DVD drive with letter $driveLetter not found." -ForegroundColor Red
return $false
}
# Check media presence and type
if (-not $discFormat.IsCurrentMediaSupported($recorder)) {
Write-Log "Current media is not supported or no media present in drive $driveLetter." -ForegroundColor Red
return $false
}
# Set up the burning session
$discFormat.Recorder = $recorder
$discFormat.ClientName = "PowerShell DVD Burning"
$discFormat.ForceMediaToBeClosed = $true
# Create a file system and add files
Write-Log "Setting up file system for burning..." -ForegroundColor Cyan
$fileSystem = New-Object -ComObject IMAPI2FS.MsftFileSystemImage
$fileSystem.ChooseImageDefaultsForMediaType(0) # DVD media
$fileSystem.Root.AddTree($folderPath, $false)
$fileSystem.VolumeName = $label
# Calculate folder size to make sure it fits
$folderSize = (Get-ChildItem $folderPath -Recurse | Measure-Object -Property Length -Sum).Sum / 1GB
$mediaCapacity = 4.7 # DVD capacity in GB
Write-Log "Folder size: $($folderSize.ToString('0.00')) GB, DVD capacity: $mediaCapacity GB" -ForegroundColor Cyan
if ($folderSize -gt $mediaCapacity) {
Write-Log "Warning: Folder size ($($folderSize.ToString('0.00')) GB) exceeds DVD capacity ($mediaCapacity GB)" -ForegroundColor Yellow
$proceed = Read-Host "Do you want to proceed anyway? (Y/N)"
Write-Log "User response to size warning: $proceed" -ForegroundColor Yellow
if ($proceed -ne "Y") {
Write-Log "Burning canceled due to size constraints." -ForegroundColor Yellow
return $false
}
}
Write-Log "Burning folder: $folderPath to drive $driveLetter" -ForegroundColor Cyan
Write-Log "Preparing media and calculating layout..." -ForegroundColor Cyan
# Register for progress events
$burnProgressEvent = Register-ObjectEvent -InputObject $discFormat -EventName "Update" -Action {
$progress = $Event.SourceArgs[0]
$percentComplete = [int]($progress.CurrentAction * 100 / 2)
Write-Progress -Activity "Burning DVD" -Status "Progress" -PercentComplete $percentComplete
# Log progress at 10% intervals
if ($percentComplete % 10 -eq 0 -and $percentComplete -ne 0) {
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$logMessage = "[$timestamp] Burning progress: $percentComplete%"
Add-Content -Path $LogFile -Value $logMessage
}
}
# Burn the files
Write-Log "Starting burn process..." -ForegroundColor Cyan
$result = $discFormat.Write($fileSystem.CreateResultImage())
# Unregister event
Unregister-Event -SourceIdentifier $burnProgressEvent.Name
# Eject the disc
Write-Log "Burn completed, ejecting media..." -ForegroundColor Green
$recorder.EjectMedia()
Write-Log "DVD burning completed successfully!" -ForegroundColor Green
return $true
}
catch {
Write-Log "Error during burning: $($_.Exception.Message)" -ForegroundColor Red
Write-Log "Stack trace: $($_.Exception.StackTrace)" -ForegroundColor Red
return $false
}
}
# Function to move a folder to the processed location
function Move-ToProcessed {
param (
[string]$sourceFolderPath,
[string]$destinationBasePath
)
try {
Write-Log "Preparing to move processed folder to destination..." -ForegroundColor Cyan
# Create destination directory if it doesn't exist
if (-not (Test-Path -Path $destinationBasePath)) {
New-Item -Path $destinationBasePath -ItemType Directory -Force | Out-Null
Write-Log "Created processed folders directory: $destinationBasePath" -ForegroundColor Yellow
}
# Get folder name from path
$folderName = Split-Path $sourceFolderPath -Leaf
$destinationPath = Join-Path -Path $destinationBasePath -ChildPath $folderName
# Check if destination already exists
if (Test-Path -Path $destinationPath) {
$timestamp = Get-Date -Format "yyyyMMdd-HHmmss"
$destinationPath = "$destinationPath-$timestamp"
Write-Log "Destination already exists. Using: $destinationPath" -ForegroundColor Yellow
}
# Move the folder
Write-Log "Moving folder from $sourceFolderPath to $destinationPath..." -ForegroundColor Cyan
Move-Item -Path $sourceFolderPath -Destination $destinationPath -Force
Write-Log "Folder successfully moved to processed location." -ForegroundColor Green
return $true
}
catch {
Write-Log "Error moving folder: $($_.Exception.Message)" -ForegroundColor Red
Write-Log "Stack trace: $($_.Exception.StackTrace)" -ForegroundColor Red
return $false
}
}
# Initialize log file with header
$startTime = Get-Date
"===================================================" | Out-File -FilePath $LogFile -Force
"DVD BURNING SESSION STARTED: $startTime" | Out-File -FilePath $LogFile -Append
"===================================================" | Out-File -FilePath $LogFile -Append
# Check if drive letter is correctly formatted
if (-not $DVDDriveLetter.EndsWith(":")) {
$DVDDriveLetter = "$($DVDDriveLetter):"
Write-Log "Formatted drive letter to: $DVDDriveLetter" -ForegroundColor Cyan
}
# Check if SourceDirectory exists
if (-not (Test-Path -Path $SourceDirectory)) {
Write-Log "Error: Source directory not found at $SourceDirectory" -ForegroundColor Red
exit 1
}
# Get all folders in the source directory
try {
Write-Log "Scanning source directory for folders..." -ForegroundColor Cyan
$foldersToBurn = Get-ChildItem -Path $SourceDirectory -Directory | Select-Object -ExpandProperty FullName
if ($foldersToBurn.Count -eq 0) {
Write-Log "Error: No folders found in the source directory." -ForegroundColor Red
exit 1
}
Write-Log "Found $($foldersToBurn.Count) folders to process." -ForegroundColor Green
}
catch {
Write-Log "Error reading source directory: $($_.Exception.Message)" -ForegroundColor Red
exit 1
}
# Display script configuration
Write-Log "`n=== DVD Burning Automation ===" -ForegroundColor Cyan
Write-Log "DVD Drive: $DVDDriveLetter" -ForegroundColor Cyan
Write-Log "Source Directory: $SourceDirectory" -ForegroundColor Cyan
Write-Log "Processed Folder: $ProcessedFolder" -ForegroundColor Cyan
Write-Log "Folders to burn: $($foldersToBurn.Count)" -ForegroundColor Cyan
Write-Log "Log File: $LogFile" -ForegroundColor Cyan
Write-Log "==========================`n" -ForegroundColor Cyan
# Main loop to process all folders
$totalFolders = $foldersToBurn.Count
$currentFolder = 0
$successCount = 0
$failCount = 0
# Create a session log file (keeping the original log file as well)
$sessionLogFile = "DVD-Burning-Log-$(Get-Date -Format 'yyyyMMdd-HHmmss').txt"
"DVD Burning Session started at $(Get-Date)" | Out-File -FilePath $sessionLogFile
"Source Directory: $SourceDirectory" | Out-File -FilePath $sessionLogFile -Append
"DVD Drive: $DVDDriveLetter" | Out-File -FilePath $sessionLogFile -Append
"Processed Folder: $ProcessedFolder" | Out-File -FilePath $sessionLogFile -Append
"Total folders to process: $totalFolders" | Out-File -FilePath $sessionLogFile -Append
"Main log file: $LogFile" | Out-File -FilePath $sessionLogFile -Append
# Process each folder
foreach ($folder in $foldersToBurn) {
$currentFolder++
$folderName = Split-Path $folder -Leaf
$processMessage = "Processing DVD $currentFolder of $totalFolders = $folderName"
Write-Log "`n====== $processMessage ======" -ForegroundColor Yellow
$processMessage | Out-File -FilePath $sessionLogFile -Append
# Prompt user to insert disc
Write-Log "Please insert a blank DVD into drive $DVDDriveLetter and press Enter to continue..." -ForegroundColor Yellow
Read-Host | Out-Null
Write-Log "User confirmed disc is ready" -ForegroundColor Cyan
# Burn the current folder
$burnStartTime = Get-Date
Write-Log "Starting burn at: $burnStartTime" -ForegroundColor Cyan
$burnSuccess = Burn-DVD -folderPath $folder -label $folderName -driveLetter $DVDDriveLetter
$burnEndTime = Get-Date
$burnDuration = $burnEndTime - $burnStartTime
Write-Log "Burn operation completed at: $burnEndTime (Duration: $($burnDuration.ToString('hh\:mm\:ss')))" -ForegroundColor Cyan
if ($burnSuccess) {
$successMessage = "DVD $currentFolder of $totalFolders completed successfully."
Write-Log $successMessage -ForegroundColor Green
$successMessage | Out-File -FilePath $sessionLogFile -Append
# Move the folder to processed location
Write-Log "Starting folder move operation..." -ForegroundColor Cyan
$moveSuccess = Move-ToProcessed -sourceFolderPath $folder -destinationBasePath $ProcessedFolder
if ($moveSuccess) {
$moveMessage = "Folder '$folderName' has been moved to processed location: $ProcessedFolder"
Write-Log $moveMessage -ForegroundColor Green
$moveMessage | Out-File -FilePath $sessionLogFile -Append
}
else {
Write-Log "Failed to move folder to processed location. It remains in its original location." -ForegroundColor Red
"ERROR: Failed to move folder $folder to $ProcessedFolder" | Out-File -FilePath $sessionLogFile -Append
$retry = Read-Host "Do you want to retry moving the folder? (Y/N)"
Write-Log "User response to retry moving: $retry" -ForegroundColor Yellow
if ($retry -eq "Y") {
Write-Log "Retrying folder move operation..." -ForegroundColor Yellow
$moveSuccess = Move-ToProcessed -sourceFolderPath $folder -destinationBasePath $ProcessedFolder
if ($moveSuccess) {
Write-Log "Retry successful - Folder moved to $ProcessedFolder" -ForegroundColor Green
"SUCCESS: Retry successful - Folder moved to $ProcessedFolder" | Out-File -FilePath $sessionLogFile -Append
}
else {
Write-Log "Retry failed - Folder not moved" -ForegroundColor Red
"ERROR: Retry failed - Folder not moved" | Out-File -FilePath $sessionLogFile -Append
}
}
}
$successCount++
}
else {
Write-Log "Failed to burn DVD $currentFolder of $totalFolders." -ForegroundColor Red
"ERROR: Failed to burn folder $folder to DVD" | Out-File -FilePath $sessionLogFile -Append
$failCount++
$retry = Read-Host "Do you want to retry burning this folder? (Y/N)"
Write-Log "User response to retry burning: $retry" -ForegroundColor Yellow
if ($retry -eq "Y") {
Write-Log "User requested retry - will process the same folder again." -ForegroundColor Yellow
$currentFolder--
continue
}
}
# If not the last folder, prompt for next disc
if ($currentFolder -lt $totalFolders) {
Write-Log "Please remove the current DVD from drive $DVDDriveLetter." -ForegroundColor Yellow
}
}
# Summary
$endTime = Get-Date
$totalDuration = $endTime - $startTime
$summary = @"
======= DVD Burning Summary =======
Total folders: $totalFolders
Successfully burned: $successCount
Failed: $failCount
DVD Drive used: $DVDDriveLetter
Processed folder: $ProcessedFolder
Started at: $startTime
Completed at: $endTime
Total duration: $($totalDuration.ToString('hh\:mm\:ss'))
Session log file: $sessionLogFile
Main log file: $LogFile
=================================
"@
Write-Log $summary -ForegroundColor Cyan
$summary | Out-File -FilePath $sessionLogFile -Append
Write-Log "`nAll folders have been processed. DVD burning task complete!" -ForegroundColor Green
# Add closing line to log file
"===================================================" | Out-File -FilePath $LogFile -Append
"DVD BURNING SESSION COMPLETED: $endTime" | Out-File -FilePath $LogFile -Append
"TOTAL DURATION: $($totalDuration.ToString('hh\:mm\:ss'))" | Out-File -FilePath $LogFile -Append
"===================================================" | Out-File -FilePath $LogFile -Append
# ExecuteScripts.ps1
# This script executes specified PowerShell scripts in sequence,
# logs execution details, and handles errors
# Function to log messages to both console and file
function Write-Log {
param (
[Parameter(Mandatory = $true)]
[string]$Message,
[string]$LogFile = "burn.log"
)
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$logMessage = "[$timestamp] $Message"
# Write to console
Write-Host $logMessage
# Write to log file
Add-Content -Path $LogFile -Value $logMessage
}
# Initialize log file (overwrite if exists)
$logFile = "burn.log"
Set-Content -Path $logFile -Value "# Script Execution Log - Started on $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')`n" -Force
# Display a 15-second countdown before starting
Write-Host "`nScript will start in 15 seconds. Press Ctrl+C to cancel...`n" -ForegroundColor Yellow
for ($i = 15; $i -gt 0; $i--) {
Write-Host "`rStarting in $i seconds..." -NoNewline -ForegroundColor Yellow
Start-Sleep -Seconds 1
}
Write-Host "`rStarting now! " -ForegroundColor Green
Write-Host ""
# Log the countdown completion
Write-Log "15-second countdown completed. Starting script execution." -LogFile $logFile
# Scripts to execute in sequence
$scripts = @(
"4.ps1"
)
# Track execution details for summary
$executionSummary = @()
# Execute each script in sequence
$overallStartTime = Get-Date
$allSuccessful = $true
foreach ($script in $scripts) {
$scriptInfo = @{
Name = $script
StartTime = $null
EndTime = $null
Duration = $null
Parameters = $null
Status = "Not Run"
}
try {
# Get script parameters if any (demo - modify as needed)
$scriptParams = @{}
# In a real scenario, you might dynamically determine parameters for each script
# Log script start
$startTime = Get-Date
$scriptInfo.StartTime = $startTime
$scriptInfo.Parameters = $scriptParams
Write-Log "Starting execution of script: $script" -LogFile $logFile
if ($scriptParams.Count -gt 0) {
$paramString = ($scriptParams.GetEnumerator() | ForEach-Object { "-$($_.Key) $($_.Value)" }) -join " "
Write-Log "Script parameters: $paramString" -LogFile $logFile
} else {
Write-Log "Script parameters: None" -LogFile $logFile
}
# Execute the script
# Using Invoke-Expression here to allow for parameter passing
$scriptPath = Join-Path -Path $PSScriptRoot -ChildPath $script
if ($scriptParams.Count -gt 0) {
# Convert parameters to string format for PowerShell
$paramStr = $scriptParams.GetEnumerator() | ForEach-Object { "-$($_.Key) '$($_.Value)'" }
$command = "& '$scriptPath' $paramStr"
Invoke-Expression $command
} else {
& $scriptPath
}
# Check if there was an error during execution
if (-not $?) {
throw "Script execution failed with a non-terminating error."
}
# Log script completion
$endTime = Get-Date
$duration = $endTime - $startTime
$scriptInfo.EndTime = $endTime
$scriptInfo.Duration = $duration
$scriptInfo.Status = "Success"
Write-Log "Successfully completed execution of script: $script" -LogFile $logFile
Write-Log "Execution duration: $($duration.TotalSeconds.ToString('F2')) seconds" -LogFile $logFile
}
catch {
# Handle error
$endTime = Get-Date
$duration = $endTime - $startTime
$scriptInfo.EndTime = $endTime
$scriptInfo.Duration = $duration
$scriptInfo.Status = "Failed"
$errorMessage = $_.Exception.Message
Write-Log "ERROR executing script: $script" -LogFile $logFile
Write-Log "Error message: $errorMessage" -LogFile $logFile
Write-Log "Execution duration before failure: $($duration.TotalSeconds.ToString('F2')) seconds" -LogFile $logFile
$allSuccessful = $false
$executionSummary += $scriptInfo
# Exit with error code
Write-Log "Exiting due to error in script: $script" -LogFile $logFile
exit 1
}
$executionSummary += $scriptInfo
}
# Calculate overall execution time
$overallEndTime = Get-Date
$overallDuration = $overallEndTime - $overallStartTime
# Log execution summary
Write-Log "`n======== EXECUTION SUMMARY ========" -LogFile $logFile
Write-Log "Overall Start Time: $overallStartTime" -LogFile $logFile
Write-Log "Overall End Time: $overallEndTime" -LogFile $logFile
Write-Log "Total Duration: $($overallDuration.TotalSeconds.ToString('F2')) seconds" -LogFile $logFile
Write-Log "`nScripts Executed:" -LogFile $logFile
foreach ($scriptInfo in $executionSummary) {
Write-Log " * $($scriptInfo.Name)" -LogFile $logFile
Write-Log " - Status: $($scriptInfo.Status)" -LogFile $logFile
Write-Log " - Start Time: $($scriptInfo.StartTime)" -LogFile $logFile
Write-Log " - End Time: $($scriptInfo.EndTime)" -LogFile $logFile
Write-Log " - Duration: $($scriptInfo.Duration.TotalSeconds.ToString('F2')) seconds" -LogFile $logFile
if ($scriptInfo.Parameters.Count -gt 0) {
$paramString = ($scriptInfo.Parameters.GetEnumerator() | ForEach-Object { "-$($_.Key) $($_.Value)" }) -join " "
Write-Log " - Parameters: $paramString" -LogFile $logFile
} else {
Write-Log " - Parameters: None" -LogFile $logFile
}
Write-Log "" -LogFile $logFile
}
# Log success message if all scripts completed successfully
if ($allSuccessful) {
Write-Log "All scripts executed successfully!" -LogFile $logFile
Write-Log "======== END OF SUMMARY ========`n" -LogFile $logFile
exit 0
}
# main.ps1
# This script executes specified PowerShell scripts in sequence,
# logs execution details, and handles errors
#
# .SYNOPSIS
# Executes PowerShell scripts in sequence with detailed logging.
#
# .DESCRIPTION
# This script runs a sequence of PowerShell scripts (1.ps1, 2.ps1, 3.ps1 by default),
# logs their execution details, handles errors, and provides a summary report.
# You can specify which script to start from using the -StartFrom parameter.
#
# .PARAMETER StartFrom
# Specifies which script number to start execution from.
# Default is 1, which runs all scripts.
# Use 2 to run from the second script, 3 to run only the third script, etc.
#
# .EXAMPLE
# .\ExecuteScripts.ps1
# Runs all scripts (1.ps1, 2.ps1, 3.ps1)
#
# .EXAMPLE
# .\ExecuteScripts.ps1 -StartFrom 2
# Runs scripts starting from the second one (2.ps1, 3.ps1)
#
# .EXAMPLE
# .\ExecuteScripts.ps1 -StartFrom 3
# Runs only the third script (3.ps1)
#
# .NOTES
# Each script execution is logged to main.log with timestamps and execution statistics.
param (
[Parameter(Mandatory = $false)]
[int]$StartFrom = 1
)
# Function to log messages to both console and file
function Write-Log {
param (
[Parameter(Mandatory = $true)]
[string]$Message,
[string]$LogFile = "main.log"
)
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$logMessage = "[$timestamp] $Message"
# Write to console
Write-Host $logMessage
# Write to log file
Add-Content -Path $LogFile -Value $logMessage
}
# Initialize log file (overwrite if exists)
$logFile = "main.log"
Set-Content -Path $logFile -Value "# Script Execution Log - Started on $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')`n" -Force
# Display a 15-second countdown before starting
Write-Host "`nScript will start in 15 seconds. Press Ctrl+C to cancel...`n" -ForegroundColor Yellow
for ($i = 15; $i -gt 0; $i--) {
Write-Host "`rStarting in $i seconds..." -NoNewline -ForegroundColor Yellow
Start-Sleep -Seconds 1
}
Write-Host "`rStarting now! " -ForegroundColor Green
Write-Host ""
# Log the countdown completion
Write-Log "15-second countdown completed. Starting script execution." -LogFile $logFile
# Scripts to execute in sequence
$allScripts = @(
"1.ps1",
"2.ps1",
"3.ps1"
)
# Filter scripts based on StartFrom parameter
$scripts = $allScripts | Where-Object { [int]($_ -replace '\.ps1$', '') -ge $StartFrom }
# Log which scripts will be executed
Write-Log "Starting execution from script number $StartFrom" -LogFile $logFile
Write-Log "Scripts that will be executed: $($scripts -join ', ')" -LogFile $logFile
# Track execution details for summary
$executionSummary = @()
# Execute each script in sequence
$overallStartTime = Get-Date
$allSuccessful = $true
foreach ($script in $scripts) {
$scriptInfo = @{
Name = $script
StartTime = $null
EndTime = $null
Duration = $null
Parameters = $null
Status = "Not Run"
}
try {
# Get script parameters if any (demo - modify as needed)
$scriptParams = @{}
# In a real scenario, you might dynamically determine parameters for each script
# Log script start
$startTime = Get-Date
$scriptInfo.StartTime = $startTime
$scriptInfo.Parameters = $scriptParams
Write-Log "Starting execution of script: $script" -LogFile $logFile
if ($scriptParams.Count -gt 0) {
$paramString = ($scriptParams.GetEnumerator() | ForEach-Object { "-$($_.Key) $($_.Value)" }) -join " "
Write-Log "Script parameters: $paramString" -LogFile $logFile
} else {
Write-Log "Script parameters: None" -LogFile $logFile
}
# Execute the script
# Using Invoke-Expression here to allow for parameter passing
$scriptPath = Join-Path -Path $PSScriptRoot -ChildPath $script
if ($scriptParams.Count -gt 0) {
# Convert parameters to string format for PowerShell
$paramStr = $scriptParams.GetEnumerator() | ForEach-Object { "-$($_.Key) '$($_.Value)'" }
$command = "& '$scriptPath' $paramStr"
Invoke-Expression $command
} else {
& $scriptPath
}
# Check if there was an error during execution
if (-not $?) {
throw "Script execution failed with a non-terminating error."
}
# Log script completion
$endTime = Get-Date
$duration = $endTime - $startTime
$scriptInfo.EndTime = $endTime
$scriptInfo.Duration = $duration
$scriptInfo.Status = "Success"
Write-Log "Successfully completed execution of script: $script" -LogFile $logFile
Write-Log "Execution duration: $($duration.TotalSeconds.ToString('F2')) seconds" -LogFile $logFile
}
catch {
# Handle error
$endTime = Get-Date
$duration = $endTime - $startTime
$scriptInfo.EndTime = $endTime
$scriptInfo.Duration = $duration
$scriptInfo.Status = "Failed"
$errorMessage = $_.Exception.Message
Write-Log "ERROR executing script: $script" -LogFile $logFile
Write-Log "Error message: $errorMessage" -LogFile $logFile
Write-Log "Execution duration before failure: $($duration.TotalSeconds.ToString('F2')) seconds" -LogFile $logFile
$allSuccessful = $false
$executionSummary += $scriptInfo
# Exit with error code
Write-Log "Exiting due to error in script: $script" -LogFile $logFile
exit 1
}
$executionSummary += $scriptInfo
}
# Calculate overall execution time
$overallEndTime = Get-Date
$overallDuration = $overallEndTime - $overallStartTime
# Log execution summary
Write-Log "`n======== EXECUTION SUMMARY ========" -LogFile $logFile
Write-Log "Overall Start Time: $overallStartTime" -LogFile $logFile
Write-Log "Overall End Time: $overallEndTime" -LogFile $logFile
Write-Log "Total Duration: $($overallDuration.TotalSeconds.ToString('F2')) seconds" -LogFile $logFile
Write-Log "`nScripts Executed:" -LogFile $logFile
foreach ($scriptInfo in $executionSummary) {
Write-Log " * $($scriptInfo.Name)" -LogFile $logFile
Write-Log " - Status: $($scriptInfo.Status)" -LogFile $logFile
Write-Log " - Start Time: $($scriptInfo.StartTime)" -LogFile $logFile
Write-Log " - End Time: $($scriptInfo.EndTime)" -LogFile $logFile
Write-Log " - Duration: $($scriptInfo.Duration.TotalSeconds.ToString('F2')) seconds" -LogFile $logFile
if ($scriptInfo.Parameters.Count -gt 0) {
$paramString = ($scriptInfo.Parameters.GetEnumerator() | ForEach-Object { "-$($_.Key) $($_.Value)" }) -join " "
Write-Log " - Parameters: $paramString" -LogFile $logFile
} else {
Write-Log " - Parameters: None" -LogFile $logFile
}
Write-Log "" -LogFile $logFile
}
# Log success message if all scripts completed successfully
if ($allSuccessful) {
Write-Log "All scripts executed successfully!" -LogFile $logFile
Write-Log "======== END OF SUMMARY ========`n" -LogFile $logFile
exit 0
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment