Last active
March 3, 2025 18:02
-
-
Save jason-riddle/6bcd4e30a40c51779931294573a03172 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<# | |
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" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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" | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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" | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<# | |
.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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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 | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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