Last active
June 16, 2025 09:55
-
-
Save koraysels/acf14bee12ae15fcd48788929ebe67e1 to your computer and use it in GitHub Desktop.
smart unzip of canvas quiz submissions
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
| #!/bin/bash | |
| # Function to check if required tools are available | |
| check_dependencies() { | |
| local missing_tools=() | |
| if ! command -v unzip &> /dev/null; then | |
| missing_tools+=("unzip") | |
| fi | |
| if ! command -v unrar &> /dev/null && ! command -v rar &> /dev/null && ! command -v unar &> /dev/null; then | |
| missing_tools+=("unrar, rar, or unar") | |
| fi | |
| if ! command -v 7z &> /dev/null && ! command -v 7za &> /dev/null; then | |
| missing_tools+=("7z or 7za") | |
| fi | |
| if [[ ${#missing_tools[@]} -gt 0 ]]; then | |
| echo "Warning: Some extraction tools are missing:" | |
| printf ' - %s\n' "${missing_tools[@]}" | |
| echo "" | |
| echo "Install missing tools:" | |
| echo " Ubuntu/Debian: sudo apt install unzip unrar-free p7zip-full" | |
| echo " CentOS/RHEL: sudo yum install unzip unrar p7zip" | |
| echo " macOS: brew install unzip unar p7zip" | |
| echo "" | |
| fi | |
| } | |
| # Function to get archive type from extension | |
| get_archive_type() { | |
| local file="$1" | |
| local extension="${file##*.}" | |
| echo "$extension" | tr '[:upper:]' '[:lower:]' # Convert to lowercase | |
| } | |
| # Function to get base name without archive extension | |
| get_base_name() { | |
| local file="$1" | |
| local base_name=$(basename "$file") | |
| # Remove various archive extensions | |
| if [[ "$base_name" =~ \.(zip|rar|7z)$ ]]; then | |
| echo "${base_name%.*}" | |
| else | |
| echo "$base_name" | |
| fi | |
| } | |
| # Function to extract archive based on type | |
| extract_archive() { | |
| local archive_file="$1" | |
| local temp_dir="$2" | |
| local archive_type=$(get_archive_type "$archive_file") | |
| case "$archive_type" in | |
| "zip") | |
| if command -v unzip &> /dev/null; then | |
| unzip -q "$archive_file" -d "$temp_dir" 2>/dev/null | |
| else | |
| echo "Error: unzip not found" | |
| return 1 | |
| fi | |
| ;; | |
| "rar") | |
| if command -v unar &> /dev/null; then | |
| # macOS preferred tool - unar extracts to current directory by default | |
| (cd "$temp_dir" && unar -q "$archive_file" 2>/dev/null) | |
| elif command -v unrar &> /dev/null; then | |
| unrar x -inul "$archive_file" "$temp_dir/" 2>/dev/null | |
| elif command -v rar &> /dev/null; then | |
| rar x -inul "$archive_file" "$temp_dir/" 2>/dev/null | |
| else | |
| echo "Error: unar/unrar/rar not found" | |
| return 1 | |
| fi | |
| ;; | |
| "7z") | |
| if command -v 7z &> /dev/null; then | |
| 7z x "$archive_file" -o"$temp_dir" -y >/dev/null 2>&1 | |
| elif command -v 7za &> /dev/null; then | |
| 7za x "$archive_file" -o"$temp_dir" -y >/dev/null 2>&1 | |
| else | |
| echo "Error: 7z/7za not found" | |
| return 1 | |
| fi | |
| ;; | |
| *) | |
| echo "Error: Unsupported archive type: $archive_type" | |
| return 1 | |
| ;; | |
| esac | |
| } | |
| # Function to extract archive and rename folder | |
| smart_extract() { | |
| local archive_file="$1" | |
| # Check if archive file exists | |
| if [[ ! -f "$archive_file" ]]; then | |
| echo "Error: Archive file '$archive_file' not found." | |
| return 1 | |
| fi | |
| # Check if it's a supported archive type | |
| local archive_type=$(get_archive_type "$archive_file") | |
| if [[ ! "$archive_type" =~ ^(zip|rar|7z)$ ]]; then | |
| echo "Error: '$archive_file' is not a supported archive type (zip, rar, 7z)." | |
| return 1 | |
| fi | |
| # Get the base name without archive extension | |
| local base_name=$(get_base_name "$archive_file") | |
| # Extract the prefix before the first sequence of numbers | |
| # This regex matches everything up to (but not including) the first digit | |
| local new_name=$(echo "$base_name" | sed 's/[0-9].*//') | |
| # Remove trailing underscore if present | |
| new_name=$(echo "$new_name" | sed 's/_$//') | |
| # Skip if target directory already exists | |
| if [[ -d "$new_name" ]]; then | |
| echo "Skipping '$archive_file' - directory '$new_name' already exists" | |
| return 0 | |
| fi | |
| echo "Extracting '$archive_file' ($(echo "$archive_type" | tr '[:lower:]' '[:upper:]'))..." | |
| # Create a temporary directory for extraction | |
| local temp_dir=$(mktemp -d) | |
| # Extract to temporary directory | |
| if ! extract_archive "$archive_file" "$temp_dir"; then | |
| echo "Error: Failed to extract '$archive_file'" | |
| rm -rf "$temp_dir" | |
| return 1 | |
| fi | |
| # Count items in temp directory (exclude hidden files starting with .) | |
| local item_count=$(ls -1 "$temp_dir" 2>/dev/null | grep -v '^\.' | wc -l) | |
| if [[ $item_count -eq 0 ]]; then | |
| echo "Warning: No files extracted from '$archive_file'" | |
| rm -rf "$temp_dir" | |
| return 1 | |
| elif [[ $item_count -eq 1 ]]; then | |
| # Single item - move and rename it | |
| local extracted_item=$(ls -1 "$temp_dir" | grep -v '^\.' | head -1) | |
| mv "$temp_dir/$extracted_item" "./$new_name" | |
| echo "✓ Extracted and renamed to: $new_name" | |
| else | |
| # Multiple items - move temp dir contents to new folder | |
| mkdir -p "./$new_name" | |
| # Move all items except hidden files | |
| find "$temp_dir" -mindepth 1 -maxdepth 1 ! -name '.*' -exec mv {} "./$new_name/" \; | |
| echo "✓ Extracted multiple items to folder: $new_name" | |
| fi | |
| # Clean up temp directory | |
| rm -rf "$temp_dir" | |
| } | |
| # Function to extract all archives in directory | |
| extract_all() { | |
| local archive_files=(*.zip *.rar *.7z *.ZIP *.RAR *.7Z) | |
| local valid_archives=() | |
| # Filter out non-existent files (when no files match the pattern) | |
| for file in "${archive_files[@]}"; do | |
| if [[ -f "$file" ]]; then | |
| valid_archives+=("$file") | |
| fi | |
| done | |
| # Check if any archive files exist | |
| if [[ ${#valid_archives[@]} -eq 0 ]]; then | |
| echo "No supported archive files (zip, rar, 7z) found in current directory." | |
| exit 1 | |
| fi | |
| echo "Found ${#valid_archives[@]} archive file(s) in current directory:" | |
| for file in "${valid_archives[@]}"; do | |
| local archive_type=$(get_archive_type "$file") | |
| echo " $file ($(echo "$archive_type" | tr '[:lower:]' '[:upper:]'))" | |
| done | |
| echo "" | |
| local success_count=0 | |
| local skip_count=0 | |
| local error_count=0 | |
| # Process each archive file | |
| for archive_file in "${valid_archives[@]}"; do | |
| local before_dir_count=$(ls -ld */ 2>/dev/null | wc -l) | |
| if smart_extract "$archive_file"; then | |
| local after_dir_count=$(ls -ld */ 2>/dev/null | wc -l) | |
| if [[ $after_dir_count -gt $before_dir_count ]]; then | |
| ((success_count++)) | |
| else | |
| ((skip_count++)) | |
| fi | |
| else | |
| ((error_count++)) | |
| fi | |
| echo "" | |
| done | |
| echo "=== Summary ===" | |
| echo "Successfully extracted: $success_count" | |
| echo "Skipped (already exists): $skip_count" | |
| echo "Errors: $error_count" | |
| echo "Total processed: ${#valid_archives[@]}" | |
| } | |
| # Main execution | |
| echo "Smart Archive Extractor (ZIP, RAR, 7Z)" | |
| echo "======================================" | |
| echo "" | |
| # Check dependencies | |
| check_dependencies | |
| if [[ $# -eq 0 ]]; then | |
| echo "Extracting all archive files in current directory..." | |
| echo "" | |
| extract_all | |
| else | |
| # Single file mode | |
| archive_type=$(get_archive_type "$1") | |
| if [[ ! "$archive_type" =~ ^(zip|rar|7z)$ ]]; then | |
| echo "Error: '$1' is not a supported archive type (zip, rar, 7z)." | |
| exit 1 | |
| fi | |
| smart_extract "$1" | |
| fi |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment