2025-02-09 02:10:26 -06:00
#!/bin/bash
# Get the absolute path to the project directory
PROJECT_DIR = " $( cd " $( dirname " ${ BASH_SOURCE [0] } " ) " && pwd ) "
CHALLENGE_DIR = ~/challenge
# Colors for output
GREEN = '\033[0;32m'
YELLOW = '\033[1;33m'
BLUE = '\033[0;34m'
2025-02-09 02:19:41 -06:00
RED = '\033[0;31m'
2025-02-09 03:25:43 -06:00
CYAN = '\033[0;36m'
BOLD = '\033[1m'
2025-02-09 02:10:26 -06:00
NC = '\033[0m'
2025-02-09 03:49:04 -06:00
# Challenge definitions
declare -A CHALLENGES =(
[ "1.1" ]= "Find all .conf files modified in the last 24 hours and save the list to recent_configs.txt|Try using find with -mtime|Find command with time filters"
[ "1.2" ]= "Generate a report showing count of files by extension|Use find and awk together|File type counting"
[ "1.3" ]= "Create directories for different file types and sort files|Look into file command|File organization"
[ "1.4" ]= "Find and list all empty files and zero-byte files|Use find with size option|File size operations"
[ "1.5" ]= "Find and handle duplicate files using md5sum|Consider sort and uniq|Duplicate handling"
)
2025-02-09 02:19:41 -06:00
# Error handling
set -o errexit # Exit on error
set -o nounset # Exit on undefined variable
set -o pipefail # Exit on pipe failure
# Trap errors and provide error message
trap 'echo -e "${RED}Error: Command failed at line $LINENO${NC}" >&2' ERR
2025-02-09 03:25:43 -06:00
# Function to show spinner while executing command
show_spinner() {
local pid = $1
local delay = 0.1
local spinstr = '|/-\'
while [ " $( ps a | awk '{print $1}' | grep $pid ) " ] ; do
local temp = ${ spinstr #? }
printf " [%c] " " $spinstr "
local spinstr = $temp ${ spinstr % " $temp " }
sleep $delay
printf "\b\b\b\b\b\b"
done
printf " \b\b\b\b"
}
# Function to print banner
print_banner() {
clear
cat << "EOF"
2025-02-09 03:43:33 -06:00
______ __ __ _
/ ____/___ ____ ___ ____ ___ ____ _____ ___/ / / / ( _) ___ ___
/ / / __ \/ __ ` __ \/ __ ` __ \/ __ ` / __ \/ __ / / / / / __ \/ _ \
/ /___/ /_/ / / / / / / / / / / / /_/ / / / / /_/ / / /___/ / / / / __/
\_ ___/\_ ___/_/ /_/ /_/_/ /_/ /_/\_ _,_/_/ /_/\_ _,_/ /_____/_/_/ /_/\_ __/
__ ___ __ ________ ____
/ | / /___ ______/ /____ _______ __ / ____/ /_ ___/ / /__ ____
/ /| _/ / __ ` / ___/ __/ _ \/ ___/ / / / / / / __ \/ __ / / _ \/ __ \
/ / / / /_/ ( __ ) /_/ __/ / / /_/ / / /___/ / / / /_/ / / __/ / / /
/_/ /_/\_ _,_/____/\_ _/\_ __/_/ \_ _, / \_ ___/_/ /_/\_ _,_/_/\_ __/_/ /_/
/____/
2025-02-09 03:25:43 -06:00
EOF
2025-02-09 03:43:33 -06:00
echo -e "\n ${ BOLD } Welcome to the Command Line Mastery Challenge! ${ NC } "
echo -e " ${ BLUE } Test your Linux command-line skills with interactive challenges ${ NC } "
echo "=============================================================="
2025-02-09 03:25:43 -06:00
}
2025-02-09 02:19:41 -06:00
# Function to check if command exists
check_command() {
if ! command -v " $1 " & >/dev/null; then
echo -e " ${ RED } Error: Required command ' $1 ' not found ${ NC } "
return 1
fi
}
# Function to check required commands
check_requirements() {
local required_commands =( "find" "grep" "awk" "sed" "mkdir" "chmod" "touch" )
local missing_commands =()
for cmd in " ${ required_commands [@] } " ; do
if ! check_command " $cmd " ; then
missing_commands +=( " $cmd " )
fi
done
if [[ ${# missing_commands [@] } -gt 0 ]] ; then
echo -e " ${ RED } Error: Missing required commands: ${ missing_commands [*] }${ NC } "
return 1
fi
2025-02-09 02:33:45 -06:00
return 0
2025-02-09 02:19:41 -06:00
}
# Function to clean up previous environment
cleanup_environment() {
echo -e " ${ BLUE } Cleaning up previous environment... ${ NC } "
if [[ -d " $CHALLENGE_DIR " ]] ; then
if ! rm -rf " $CHALLENGE_DIR " 2>/dev/null; then
echo -e " ${ RED } Error: Failed to remove directory: $CHALLENGE_DIR ${ NC } "
echo -e " ${ YELLOW } Tip: Check if you have proper permissions ${ NC } "
return 1
fi
echo -e " ${ GREEN } Previous environment cleaned up ${ NC } "
fi
}
2025-02-09 02:10:26 -06:00
# Function to setup the environment
setup_environment() {
echo -e " ${ BLUE } Setting up challenge environment... ${ NC } "
2025-02-09 02:19:41 -06:00
# Check requirements first
if ! check_requirements; then
echo -e " ${ RED } Error: Setup failed - missing requirements ${ NC } "
return 1
2025-02-09 02:33:45 -06:00
fi
2025-02-09 02:10:26 -06:00
2025-02-09 02:19:41 -06:00
# Clean up first
cleanup_environment
2025-02-09 02:10:26 -06:00
2025-02-09 02:19:41 -06:00
# Create directory structure with error checking
local dirs =(
" $CHALLENGE_DIR " /{ files,backup,temp}
" $CHALLENGE_DIR /.tracking/data"
)
for dir in " ${ dirs [@] } " ; do
if ! mkdir -p " $dir " ; then
echo -e " ${ RED } Error: Failed to create directory: $dir ${ NC } "
return 1
fi
done
# Create project directories with error checking
for i in { 1..5} ; do
local project_dirs =(
" $CHALLENGE_DIR /files/project $i " /{ src,config,logs,data}
)
for dir in " ${ project_dirs [@] } " ; do
if ! mkdir -p " $dir " ; then
echo -e " ${ RED } Error: Failed to create project directory: $dir ${ NC } "
return 1
fi
done
done
# Copy setup scripts with error checking
if ! cp " $PROJECT_DIR " /setup/*.sh " $CHALLENGE_DIR /.tracking/" 2>/dev/null; then
echo -e " ${ RED } Error: Failed to copy setup scripts ${ NC } "
echo -e " ${ YELLOW } Tip: Check if setup scripts exist in: $PROJECT_DIR /setup/ ${ NC } "
return 1
fi
# Set permissions with error checking
if ! chmod +x " $CHALLENGE_DIR /.tracking/" *.sh 2>/dev/null; then
echo -e " ${ RED } Error: Failed to set script permissions ${ NC } "
return 1
fi
# Initialize tracking files with error checking
2025-02-09 02:10:26 -06:00
local data_dir = " $CHALLENGE_DIR /.tracking/data"
2025-02-09 02:19:41 -06:00
local tracking_files =( progress score challenge commands time hints_used achievements attempts)
for file in " ${ tracking_files [@] } " ; do
if ! touch " $data_dir / $file .csv" 2>/dev/null; then
echo -e " ${ RED } Error: Failed to create tracking file: $file .csv ${ NC } "
return 1
fi
done
2025-02-09 02:10:26 -06:00
echo -e " ${ GREEN } Environment setup complete! ${ NC } "
}
2025-02-09 02:19:41 -06:00
# Function to validate challenge ID
validate_challenge_id() {
local id = $1
if [[ ! $id = ~ ^[ 1-9] \. [ 1-5] $ ]] ; then
echo -e " ${ RED } Error: Invalid challenge ID format ${ NC } "
echo -e " ${ YELLOW } Tip: Challenge ID should be in format 'level.number' (e.g., 1.1) ${ NC } "
return 1
fi
}
2025-02-09 03:49:04 -06:00
# Function to get challenge description
get_challenge_description() {
local challenge_id = $1
if [[ -n " ${ CHALLENGES [ $challenge_id ] :- } " ]] ; then
echo " ${ CHALLENGES [ $challenge_id ]%%|* } "
else
echo "Challenge not found"
fi
}
# Function to get challenge hint
get_challenge_hint() {
local challenge_id = $1
if [[ -n " ${ CHALLENGES [ $challenge_id ] :- } " ]] ; then
local hint = $( echo " ${ CHALLENGES [ $challenge_id ] } " | cut -d'|' -f2)
echo -e " ${ YELLOW } Hint: ${ hint }${ NC } "
fi
}
# Function to verify challenge
verify_challenge() {
local challenge_id = $1
case " $challenge_id " in
"1.1" )
if [[ -f " $CHALLENGE_DIR /recent_configs.txt" ]] ; then
local count = $( wc -l < " $CHALLENGE_DIR /recent_configs.txt" )
if [[ $count -gt 0 ]] ; then
echo -e " ${ GREEN } Success! You found $count configuration files. ${ NC } "
return 0
fi
fi
echo -e " ${ RED } Not quite right. Make sure to save the list to recent_configs.txt ${ NC } "
;;
"1.2" )
if [[ -f " $CHALLENGE_DIR /file_report.txt" ]] ; then
echo -e " ${ GREEN } Report generated successfully! ${ NC } "
return 0
fi
echo -e " ${ RED } No report file found. Create file_report.txt with the counts ${ NC } "
;;
*)
echo -e " ${ RED } Verification not implemented for this challenge yet ${ NC } "
;;
esac
return 1
}
2025-02-09 03:25:43 -06:00
# Function to handle commands with improved feedback
handle_command() {
local cmd = " $1 "
shift # Remove first argument, leaving remaining args
case " $cmd " in
"verify" | "verify-challenge" | "v" )
echo -e " ${ CYAN } Verifying your solution... ${ NC } "
2025-02-09 03:49:04 -06:00
verify_challenge " $CURRENT_CHALLENGE_ID "
2025-02-09 03:25:43 -06:00
;;
"hint" | "show-hint" | "h" )
echo -e " ${ CYAN } Getting hint... ${ NC } "
2025-02-09 03:49:04 -06:00
get_challenge_hint " $CURRENT_CHALLENGE_ID "
2025-02-09 03:25:43 -06:00
;;
"status" | "show-status" | "s" )
2025-02-09 03:49:04 -06:00
echo -e " ${ CYAN } Current Challenge Status: ${ NC } "
echo -e "Challenge: $CURRENT_CHALLENGE_ID "
echo -e "Task: $( get_challenge_description " $CURRENT_CHALLENGE_ID " ) "
echo -e "Working Directory: $CHALLENGE_DIR "
2025-02-09 03:25:43 -06:00
;;
"clear" | "cls" | "c" )
clear
print_challenge_header " $CURRENT_CHALLENGE_ID "
;;
"reset" | "r" )
echo -e " ${ YELLOW } Resetting current challenge... ${ NC } "
setup_challenge_env " $CURRENT_CHALLENGE_ID "
;;
"exit" | "quit" | "q" )
return 1
;;
"help" | "?" )
print_help_menu
;;
2025-02-09 03:49:04 -06:00
"ls" | "pwd" | "cd" | "find" | "grep" | "awk" | "sed" | "cat" | "touch" | "mkdir" | "rm" | "mv" | "cp" )
# Execute actual command
$cmd " $@ "
;;
2025-02-09 03:25:43 -06:00
"" )
# Empty command, just continue
;;
*)
2025-02-09 03:49:04 -06:00
if command -v " $cmd " >/dev/null 2>& 1; then
$cmd " $@ "
else
echo -e " ${ RED } Unknown command: $cmd ${ NC } "
echo -e "Type ${ YELLOW } help ${ NC } or ${ YELLOW } ? ${ NC } for available commands"
fi
2025-02-09 03:25:43 -06:00
;;
esac
return 0
}
# Function to print help menu
print_help_menu() {
echo -e "\n ${ BOLD } Available Commands: ${ NC } "
echo -e " ${ YELLOW } verify, v ${ NC } - Check your solution"
echo -e " ${ YELLOW } hint, h ${ NC } - Get a hint"
echo -e " ${ YELLOW } status, s ${ NC } - Show challenge status"
echo -e " ${ YELLOW } clear, c ${ NC } - Clear screen"
echo -e " ${ YELLOW } reset, r ${ NC } - Reset current challenge"
echo -e " ${ YELLOW } help, ? ${ NC } - Show this help message"
echo -e " ${ YELLOW } exit, q ${ NC } - Exit the challenge"
echo -e "\n ${ BOLD } Tips: ${ NC } "
echo -e "- Commands can be abbreviated (e.g., 'v' for verify)"
echo -e "- Use TAB for command completion"
echo -e "- Use arrow keys for command history"
}
# Function to print challenge header
print_challenge_header() {
local challenge_id = $1
echo -e " ${ GREEN } 🎮 Challenge $challenge_id ${ NC } "
echo -e " ${ BLUE } $( get_challenge_description " $challenge_id " ) ${ NC } "
echo -e " ${ YELLOW } Type 'help' or '?' for available commands ${ NC } "
}
# Function to setup challenge environment
setup_challenge_env() {
local challenge_id = $1
2025-02-09 03:31:44 -06:00
echo -e " ${ BLUE } Setting up challenge files... ${ NC } "
2025-02-09 03:43:33 -06:00
2025-02-09 03:46:16 -06:00
# Create necessary directories
mkdir -p " $CHALLENGE_DIR /files"
2025-02-09 03:43:33 -06:00
2025-02-09 03:46:16 -06:00
# Setup files for the challenge
case " $challenge_id " in
"1.1" )
# Create some .conf files with different timestamps
for i in { 1..15} ; do
echo "config $i =value $i " > " $CHALLENGE_DIR /files/config $i .conf"
# Set random timestamps within the last 48 hours
touch -d "@ $(($( date +%s) - RANDOM % 172800 )) " " $CHALLENGE_DIR /files/config $i .conf"
done
;;
"1.2" )
# Create various file types
for ext in txt conf log py js json yml; do
for i in { 1..5} ; do
echo "content" > " $CHALLENGE_DIR /files/file $i . $ext "
done
done
;;
2025-02-09 03:49:04 -06:00
"1.3" )
# Create mixed files for organization
local files =( "doc1.txt" "script.py" "data.json" "config.yml" "log.log" "notes.md" )
for file in " ${ files [@] } " ; do
echo "content" > " $CHALLENGE_DIR /files/ $file "
done
;;
"1.4" )
# Create mix of empty and non-empty files
for i in { 1..10} ; do
if (( i % 2 == 0)) ; then
touch " $CHALLENGE_DIR /files/empty $i .txt"
else
echo "content" > " $CHALLENGE_DIR /files/file $i .txt"
fi
done
;;
"1.5" )
# Create some duplicate files
for i in { 1..5} ; do
echo "unique content $i " > " $CHALLENGE_DIR /files/file $i .txt"
cp " $CHALLENGE_DIR /files/file $i .txt" " $CHALLENGE_DIR /files/file $i .copy.txt"
done
;;
2025-02-09 03:46:16 -06:00
esac
2025-02-09 03:43:33 -06:00
echo -e " ${ GREEN } Challenge environment ready! ${ NC } "
2025-02-09 03:25:43 -06:00
return 0
}
2025-02-09 03:28:16 -06:00
# Function to source required scripts
source_required_scripts() {
local required_scripts =( common.sh challenges.sh)
for script in " ${ required_scripts [@] } " ; do
if ! source " $CHALLENGE_DIR /.tracking/ $script " 2>/dev/null; then
echo -e " ${ RED } Error: Required script not found: $script ${ NC } "
echo -e " ${ YELLOW } Running setup first... ${ NC } "
setup_environment
if ! source " $CHALLENGE_DIR /.tracking/ $script " 2>/dev/null; then
echo -e " ${ RED } Error: Failed to source $script even after setup ${ NC } "
return 1
fi
fi
done
return 0
}
2025-02-09 02:10:26 -06:00
# Function to start a challenge
start_challenge() {
local challenge_id = $1
2025-02-09 03:25:43 -06:00
export CURRENT_CHALLENGE_ID = $challenge_id
2025-02-09 02:10:26 -06:00
2025-02-09 02:19:41 -06:00
# Validate challenge ID
2025-02-09 03:49:04 -06:00
if [[ ! $challenge_id = ~ ^[ 1-9] \. [ 1-5] $ ]] ; then
echo -e " ${ RED } Error: Invalid challenge ID format ${ NC } "
echo -e " ${ YELLOW } Tip: Challenge ID should be in format 'level.number' (e.g., 1.1) ${ NC } "
2025-02-09 03:25:43 -06:00
return 1
fi
2025-02-09 02:10:26 -06:00
2025-02-09 03:25:43 -06:00
print_banner
# Setup challenge environment
echo -e " ${ BLUE } Setting up challenge environment... ${ NC } "
2025-02-09 03:31:44 -06:00
if ! setup_challenge_env " $challenge_id " ; then
return 1
fi
2025-02-09 02:10:26 -06:00
2025-02-09 03:49:04 -06:00
if [[ -z " ${ CHALLENGES [ $challenge_id ] :- } " ]] ; then
2025-02-09 02:19:41 -06:00
echo -e " ${ RED } Error: Challenge $challenge_id not found ${ NC } "
echo -e " ${ YELLOW } Available challenges: ${ NC } "
list_challenges
return 1
fi
2025-02-09 02:10:26 -06:00
2025-02-09 03:25:43 -06:00
print_challenge_header " $challenge_id "
2025-02-09 02:10:26 -06:00
2025-02-09 03:25:43 -06:00
# Enable command history and tab completion
bind 'set completion-ignore-case on' 2>/dev/null || true
bind 'set show-all-if-ambiguous on' 2>/dev/null || true
2025-02-09 03:43:33 -06:00
echo -e "\n ${ YELLOW } Your challenge environment is ready! ${ NC } "
2025-02-09 03:46:16 -06:00
echo -e " ${ BLUE } Current working directory: $CHALLENGE_DIR ${ NC } "
echo -e " ${ BLUE } Challenge: $( get_challenge_description " $challenge_id " ) ${ NC } "
echo -e "\nAvailable commands:"
2025-02-09 03:43:33 -06:00
print_help_menu
2025-02-09 03:25:43 -06:00
# Start command loop with improved prompt
local cmd_count = 0
while true; do
cmd_count = $(( cmd_count + 1 ))
echo -ne "\n ${ GREEN } challenge ${ BLUE } [ $challenge_id ] ${ GREEN } > ${ NC } "
2025-02-09 03:49:04 -06:00
read -e -r cmd args || true
2025-02-09 03:46:16 -06:00
# Handle Ctrl+D (EOF)
if [ $? -ne 0 ] ; then
echo -e "\n ${ YELLOW } Use 'exit' or 'q' to quit ${ NC } "
continue
fi
# Add command to history
history -s " $cmd $args "
# Handle the command
2025-02-09 03:25:43 -06:00
if ! handle_command " $cmd " $args ; then
break
fi
done
echo -e "\n ${ GREEN } Challenge session ended! ${ NC } "
echo -e "Commands used: $cmd_count "
2025-02-09 02:10:26 -06:00
}
2025-02-09 03:49:04 -06:00
# Function to list available challenges
list_challenges() {
echo -e "\n ${ BLUE } Available Challenges: ${ NC } "
for challenge_id in " ${ !CHALLENGES[@] } " ; do
echo -e "\n ${ YELLOW } Challenge $challenge_id ${ NC } "
echo "Task: $( get_challenge_description " $challenge_id " ) "
done
}
2025-02-09 02:10:26 -06:00
# Function to show help
show_help() {
cat << EOF
Usage: $0 [command] [options]
Commands:
start <challenge-id> Start a specific challenge
list List available challenges
setup Setup/reset the environment
2025-02-09 02:19:41 -06:00
clean Remove all challenge files
2025-02-09 02:10:26 -06:00
help Show this help message
Examples:
$0 setup # First time setup
2025-02-09 02:19:41 -06:00
$0 clean # Remove all challenge files
2025-02-09 02:10:26 -06:00
$0 list # Show available challenges
$0 start 1.1 # Start challenge 1.1
EOF
}
2025-02-09 02:19:41 -06:00
# Main execution with error handling
main() {
2025-02-09 02:33:45 -06:00
if [[ $# -eq 0 ]] ; then
show_help
return 0
fi
2025-02-09 02:19:41 -06:00
case " $1 " in
"setup" )
2025-02-09 02:10:26 -06:00
setup_environment
2025-02-09 02:19:41 -06:00
;;
"clean" )
cleanup_environment
;;
"start" )
if [[ -z " ${ 2 :- } " ]] ; then
echo -e " ${ RED } Error: Please specify a challenge ID ${ NC } "
echo "Example: $0 start 1.1"
return 1
fi
start_challenge " $2 "
;;
"list" )
2025-02-09 02:10:26 -06:00
list_challenges
2025-02-09 02:19:41 -06:00
;;
2025-02-09 02:33:45 -06:00
"help" | "--help" | "-h" )
2025-02-09 02:19:41 -06:00
show_help
;;
*)
echo -e " ${ RED } Unknown command: $1 ${ NC } "
show_help
return 1
;;
esac
}
# Run main with error handling
2025-02-09 03:49:04 -06:00
if ! main " $@ " ; then
2025-02-09 02:19:41 -06:00
echo -e " ${ RED } Command failed. See errors above. ${ NC } "
exit 1
fi