#!/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