Extract various archives with PowerShell

Nowadays, instead of using .zip, .rar or .7z files, I find myself using virtual harddrives (.vhdx) more and more as it’s so much more convenient. However, extracting large amounts of compressed files, can be a very time consuming endeavor. Here a quick and dirty PowerShell script to do this. I’ve added a check for determining if the archive one folder in the root of the archive or if it contains multiple files and folders. If it contains just the one folder, the script won’t create a new folder and extract that one folder into the new folder. Instead it will “extract here”. If multiple files and/or folders are found, it will create a new folder based on the archive name. The script assumes you have 7zip installed on the default location.

It will also create a logfile within the destination folder.

Note: Make sure you change the paths to match your environment. A backslash behind the directory path and destination path, is mandatory, as are the quotes. The paths can be local directories as well as network shares.

# Define the path to the 7-Zip executable
$sevenZipPath = "C:\Program Files\7-Zip\7z.exe"

# Define the directory containing the compressed files
$directoryPath = "<path to the archives\>"

# Define the destination directory for extraction
$destinationPath = "<path to the destination\>"

Write-Host "Scanning Archives... Please wait"

# Get all compressed files in the directory
$compressedFiles = Get-ChildItem -Path $directoryPath -Include *.rar, *.7z, *.zip -Recurse

Write-Host "Extracting Archives..."

# Initialize progress bar variables
$progress = 0
$totalFiles = $compressedFiles.Count

# Get the current date in YYYYMMDD format for log file prefix
$logDatePrefix = Get-Date -Format "yyyyMMdd"

# Define the directory for the log file
$logDirectory = "$destinationPath"

# Create the log directory if it doesn't exist
if (-not (Test-Path -Path $logDirectory)) {
    New-Item -Path $logDirectory -ItemType Directory | Out-Null
}

# Create log file path
$logFilePath = Join-Path -Path $logDirectory -ChildPath "$logDatePrefix-7zip-Uncompress.log"

# Loop through each compressed file
foreach ($compressedFile in $compressedFiles) {
    # Update progress bar
    $progress++
    $status = "Extracting " + $compressedFile.Name
    Write-Progress -Activity "Extracting Files" -Status $status -PercentComplete (($progress / $totalFiles) * 100)

    # Define the extraction folder
    $extractionFolder = Join-Path -Path $destinationPath -ChildPath ($compressedFile.BaseName)

    # Create the extraction folder if it doesn't exist
    if (-not (Test-Path -Path $extractionFolder)) {
        New-Item -Path $extractionFolder -ItemType Directory | Out-Null
    }

    # Check the file extension and extract accordingly
    if ($compressedFile.Extension -eq ".rar" -or $compressedFile.Extension -eq ".7z" -or $compressedFile.Extension -eq ".zip") {
        # Determine if the archive contains only one folder
        $listOutput = & $sevenZipPath l $compressedFile.FullName
        if ($null -eq $listOutput) {
            Write-Host "Error: Failed to list contents of $($compressedFile.Name). Skipping extraction."
            Add-Content -Path $logFilePath -Value "Error: Failed to list contents of $($compressedFile.Name). Skipping extraction."
            continue
        }
        
        $numFolders = ($listOutput | Select-String -Pattern '^(\d+ folders)$').Matches.Groups[1].Value -replace '\D'

        if ($numFolders -eq 1) {
            $command = "& `"$sevenZipPath`" x `"$($compressedFile.FullName)`" -o`"$destinationPath`" -y"
        } else {
            $command = "& `"$sevenZipPath`" x `"$($compressedFile.FullName)`" -o`"$extractionFolder`" -y"
        }
    }
    else {
        Write-Host "Unsupported file format: $($compressedFile.Extension)"
        Add-Content -Path $logFilePath -Value "Unsupported file format: $($compressedFile.Extension)"
        continue
    }

    # Execute the extraction command and write to log file
    Invoke-Expression $command | Out-File -FilePath $logFilePath -Append
}

# Final message
Write-Progress -Activity "Extracting Files" -Completed
Write-Host "All files have been extracted."
Write-Host "Log file created at: $logFilePath"

Side note: I’ve been asked various times why I choose to write out the numbers in my variables. I do this to avoid confusion, mainly my own. This is why the variable for the 7zip path is “$sevenZipPath” and not “7ZipPath”. It’s not a lot more work, it looks more elegant and it makes life easier for me.

Leave a Reply

Your email address will not be published. Required fields are marked *