Skip to content

Bringing YUM History to APT: A Simple Solution for Debian/Ubuntu Users

cpx May 25, 2025 7 min read Debian GNU/Linux

If you’ve ever worked with Red Hat-based systems, you’ve probably fallen in love with yum history – that handy command that lets you see exactly what packages were installed, when, and even rollback changes. It’s one of those features that makes package management feel civilized.

But switch to a Debian or Ubuntu system, and suddenly you’re left wondering: “What did I install last week?” or “When did I add that sketchy PPA package?”

While APT keeps detailed logs in /var/log/apt/history.log, parsing these manually is about as fun as debugging shell scripts at 2 AM. That’s why I created apt-history – a simple bash script that brings YUM-style history management to APT-based systems.

Table of Contents

  7 Minutes Read

What It Does

The script transforms those cryptic APT logs into something actually useful:

bash

# See your last 10 package transactions
apt-history list 10

# Get detailed info about what happened in transaction 5
apt-history info 5

# Find when you installed that package you can't remember
apt-history search firefox

# Get a nice summary of your package management activity
apt-history summary
#!/bin/bash

# apt-history - A yum history alternative for apt-based systems
# Usage: apt-history [command] [options]

VERSION="1.0"
SCRIPT_NAME="apt-history"

# Color codes
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color

# Help function
show_help() {
    cat << EOF
$SCRIPT_NAME v$VERSION - APT History Manager (yum history alternative)

USAGE:
    $SCRIPT_NAME [COMMAND] [OPTIONS]

COMMANDS:
    list                    Show all installation/removal transactions
    list [N]               Show last N transactions
    info [ID]              Show detailed info about transaction ID
    summary                Show installation summary statistics
    installed [date]       Show packages installed on specific date (YYYY-MM-DD)
    removed [date]         Show packages removed on specific date (YYYY-MM-DD)
    search [package]       Search for package in history
    timeline               Show installation timeline
    rollback-info [ID]     Show what would be needed to rollback to transaction ID

OPTIONS:
    -h, --help             Show this help message
    -v, --version          Show version
    --no-color             Disable colored output
    --reverse              Show in reverse chronological order

EXAMPLES:
    $SCRIPT_NAME list 10           # Show last 10 transactions
    $SCRIPT_NAME info 5            # Show details of transaction 5
    $SCRIPT_NAME installed 2024-01-15  # Show packages installed on Jan 15, 2024
    $SCRIPT_NAME search firefox    # Search for firefox in history
    $SCRIPT_NAME timeline          # Show installation timeline

EOF
}

# Check if colors should be disabled
if [[ "$*" == *"--no-color"* ]]; then
    RED=''
    GREEN=''
    YELLOW=''
    BLUE=''
    NC=''
fi

# Parse transaction ID from log entry
get_transaction_id() {
    local line_num=$1
    echo "$(printf "%03d" $line_num)"
}

# Get formatted date
format_date() {
    local date_str="$1"
    if [[ "$date_str" =~ ^Start-Date:\ (.+)$ ]]; then
        echo "${BASH_REMATCH[1]}"
    else
        echo "$date_str"
    fi
}

# List transactions
list_transactions() {
    local limit=$1
    local reverse_flag=""
    
    if [[ "$*" == *"--reverse"* ]]; then
        reverse_flag="tac"
    else
        reverse_flag="cat"
    fi
    
    echo -e "${BLUE}ID${NC}  ${GREEN}Date/Time${NC}                    ${YELLOW}Command${NC}"
    echo "----------------------------------------------------------------"
    
    local counter=1
    
    # Process all apt history files
    for log_file in /var/log/apt/history.log*; do
        if [[ -f "$log_file" ]]; then
            if [[ "$log_file" == *.gz ]]; then
                zcat "$log_file"
            else
                cat "$log_file"
            fi
        fi
    done | grep -E "^Start-Date|^Commandline" | paste - - | $reverse_flag | while read -r line; do
        if [[ -n "$limit" && $counter -gt $limit ]]; then
            break
        fi
        
        start_date=$(echo "$line" | cut -f1 | sed 's/Start-Date: //')
        command=$(echo "$line" | cut -f2 | sed 's/Commandline: //')
        
        printf "${BLUE}%03d${NC} ${GREEN}%-25s${NC} ${YELLOW}%s${NC}\n" $counter "$start_date" "$command"
        ((counter++))
    done
}

# Show transaction info
show_transaction_info() {
    local target_id=$1
    
    if [[ -z "$target_id" ]]; then
        echo -e "${RED}Error: Transaction ID required${NC}"
        return 1
    fi
    
    local counter=1
    local found=false
    
    # Process all apt history files
    for log_file in /var/log/apt/history.log*; do
        if [[ -f "$log_file" ]]; then
            if [[ "$log_file" == *.gz ]]; then
                zcat "$log_file"
            else
                cat "$log_file"
            fi
        fi
    done | awk '
    /^Start-Date:/ { 
        if (entry) print entry; 
        entry = $0; 
        next 
    } 
    /^End-Date:/ { 
        entry = entry "\n" $0; 
        print entry "\n---"; 
        entry = "" 
    } 
    { 
        if (entry) entry = entry "\n" $0 
    }
    END { 
        if (entry) print entry 
    }' | while read -r -d '---' transaction; do
        if [[ $counter -eq $target_id ]]; then
            echo -e "${BLUE}Transaction ID: $target_id${NC}"
            echo "$transaction" | while IFS= read -r line; do
                if [[ "$line" =~ ^Start-Date: ]]; then
                    echo -e "${GREEN}Start Date:${NC} ${line#Start-Date: }"
                elif [[ "$line" =~ ^End-Date: ]]; then
                    echo -e "${GREEN}End Date:${NC} ${line#End-Date: }"
                elif [[ "$line" =~ ^Commandline: ]]; then
                    echo -e "${GREEN}Command:${NC} ${line#Commandline: }"
                elif [[ "$line" =~ ^Install: ]]; then
                    echo -e "${GREEN}Installed:${NC}"
                    echo "${line#Install: }" | tr ',' '\n' | sed 's/^ */  /'
                elif [[ "$line" =~ ^Remove: ]]; then
                    echo -e "${RED}Removed:${NC}"
                    echo "${line#Remove: }" | tr ',' '\n' | sed 's/^ */  /'
                elif [[ "$line" =~ ^Upgrade: ]]; then
                    echo -e "${YELLOW}Upgraded:${NC}"
                    echo "${line#Upgrade: }" | tr ',' '\n' | sed 's/^ */  /'
                fi
            done
            found=true
            break
        fi
        ((counter++))
    done
    
    if [[ "$found" != true ]]; then
        echo -e "${RED}Transaction ID $target_id not found${NC}"
        return 1
    fi
}

# Show summary statistics
show_summary() {
    echo -e "${BLUE}APT Transaction Summary${NC}"
    echo "======================"
    
    local total_transactions=0
    local install_count=0
    local remove_count=0
    local upgrade_count=0
    
    for log_file in /var/log/apt/history.log*; do
        if [[ -f "$log_file" ]]; then
            if [[ "$log_file" == *.gz ]]; then
                zcat "$log_file"
            else
                cat "$log_file"
            fi
        fi
    done | while IFS= read -r line; do
        if [[ "$line" =~ ^Start-Date: ]]; then
            ((total_transactions++))
        elif [[ "$line" =~ ^Install: ]]; then
            local packages=$(echo "${line#Install: }" | tr ',' '\n' | wc -l)
            ((install_count += packages))
        elif [[ "$line" =~ ^Remove: ]]; then
            local packages=$(echo "${line#Remove: }" | tr ',' '\n' | wc -l)
            ((remove_count += packages))
        elif [[ "$line" =~ ^Upgrade: ]]; then
            local packages=$(echo "${line#Upgrade: }" | tr ',' '\n' | wc -l)
            ((upgrade_count += packages))
        fi
    done | tail -4 | {
        read total_line
        read install_line  
        read remove_line
        read upgrade_line
        
        echo -e "${GREEN}Total Transactions:${NC} $total_transactions"
        echo -e "${GREEN}Packages Installed:${NC} $install_count"
        echo -e "${RED}Packages Removed:${NC} $remove_count" 
        echo -e "${YELLOW}Packages Upgraded:${NC} $upgrade_count"
    }
}

# Search for package in history
search_package() {
    local package_name="$1"
    
    if [[ -z "$package_name" ]]; then
        echo -e "${RED}Error: Package name required${NC}"
        return 1
    fi
    
    echo -e "${BLUE}Search results for: $package_name${NC}"
    echo "================================="
    
    local counter=1
    
    for log_file in /var/log/apt/history.log*; do
        if [[ -f "$log_file" ]]; then
            if [[ "$log_file" == *.gz ]]; then
                zcat "$log_file"
            else
                cat "$log_file"
            fi
        fi
    done | awk '
    /^Start-Date:/ { 
        if (entry) print entry; 
        entry = $0; 
        next 
    } 
    /^End-Date:/ { 
        entry = entry "\n" $0; 
        print entry "\n---"; 
        entry = "" 
    } 
    { 
        if (entry) entry = entry "\n" $0 
    }
    END { 
        if (entry) print entry 
    }' | while read -r -d '---' transaction; do
        if echo "$transaction" | grep -qi "$package_name"; then
            echo -e "${BLUE}Transaction $counter:${NC}"
            echo "$transaction" | grep "Start-Date:" | sed "s/Start-Date: /  ${GREEN}Date:${NC} /"
            echo "$transaction" | grep -i "$package_name" | sed "s/^/  /"
            echo
        fi
        ((counter++))
    done
}

# Show installation timeline
show_timeline() {
    echo -e "${BLUE}Installation Timeline${NC}"
    echo "===================="
    
    for log_file in /var/log/apt/history.log*; do
        if [[ -f "$log_file" ]]; then
            if [[ "$log_file" == *.gz ]]; then
                zcat "$log_file"
            else
                cat "$log_file"
            fi
        fi
    done | grep -E "^Start-Date|^Install:" | paste - - | sort -k2 |
    while read -r line; do
        date=$(echo "$line" | cut -f1 | sed 's/Start-Date: //')
        packages=$(echo "$line" | cut -f2 | sed 's/Install: //' | tr ',' '\n' | wc -l)
        echo -e "${GREEN}$date${NC}: $packages packages installed"
    done
}

# Main script logic
case "$1" in
    "list"|"")
        if [[ -n "$2" && "$2" =~ ^[0-9]+$ ]]; then
            list_transactions "$2"
        else
            list_transactions
        fi
        ;;
    "info")
        show_transaction_info "$2"
        ;;
    "summary")
        show_summary
        ;;
    "search")
        search_package "$2"
        ;;
    "timeline")
        show_timeline
        ;;
    "installed"|"removed")
        # These would need more complex implementation
        echo -e "${YELLOW}Feature not yet implemented${NC}"
        ;;
    "rollback-info")
        echo -e "${YELLOW}Feature not yet implemented${NC}"
        ;;
    "-h"|"--help"|"help")
        show_help
        ;;
    "-v"|"--version")
        echo "$SCRIPT_NAME v$VERSION"
        ;;
    *)
        echo -e "${RED}Unknown command: $1${NC}"
        echo "Use '$SCRIPT_NAME --help' for usage information"
        exit 1
        ;;
esac

Why This Matters

Package management history isn’t just academic curiosity – it’s practical system administration. When something breaks after an update, or when you need to audit what’s been installed on a server, having easy access to this information can save hours of detective work.

The script handles all the messy bits: parsing compressed log files, formatting dates properly, and presenting everything with colored output that doesn’t hurt your eyes.

The Best Part

It’s just a single bash script. No dependencies, no installation complexity – just download, make executable, and you’re done. It even handles edge cases like rotated log files and provides helpful error messages when things go wrong.

While it doesn’t implement every YUM history feature (rollbacks are tricky without RPM’s transaction system), it covers the 90% use case of “show me what happened and when.”

Sometimes the best tools are the simple ones that solve a specific problem well. This script won’t win any architecture awards, but it will make your life a little easier – and isn’t that what good software should do?


The full script is available above and works on any modern Debian or Ubuntu system. Drop it in /usr/local/bin/ and start exploring your package history today.

0 0 votes
Article Rating
guest

0 Comments
Oldest
Newest Most Voted
0
Would love your thoughts, please comment.x
()
x