Back to Blog

Zsh CLI Tutorial — getopts, Colors, Progress Bars & Todo App #26

Sandy LaneSandy Lane

Video: Zsh CLI Tutorial — getopts, Colors, Progress Bars & Todo App #26 by Taught by Celeste AI - AI Coding Coach

Watch full page →

Zsh CLI Tutorial — getopts, Colors, Progress Bars & Todo App

This tutorial demonstrates how to create polished command-line tools in Zsh by parsing flags with getopts, adding colorful output using ANSI escape codes, and enhancing user experience with spinners and progress bars. It culminates in a complete todo app featuring subcommands and file-backed storage.

Code

#!/bin/zsh

# Usage function to display help message
usage() {
  echo "Usage: $0 [-v] [-n NAME] [-h] <subcommand> [args]"
  echo "Options:"
  echo "  -v          Enable verbose mode"
  echo "  -n NAME     Specify a name"
  echo "  -h          Show this help message"
  echo "Subcommands:"
  echo "  add <task>       Add a new todo task"
  echo "  list                 List all tasks"
  echo "  done <task_num> Mark task as done"
  echo "  remove <task_num> Remove a task"
  exit 2
}

# Color helper functions using ANSI escape codes
success() { echo -e "\e[32m$1\e[0m"; }
error()   { echo -e "\e[31m$1\e[0m"; }
warn()    { echo -e "\e[33m$1\e[0m"; }
info()    { echo -e "\e[34m$1\e[0m"; }

# Spinner animation function
spinner() {
  local pid=$1
  local delay=0.1
  local spinstr='|/-\'
  while kill -0 $pid 2>/dev/null; do
    for i in {1..4}; do
      printf "\r%s" "${spinstr:i-1:1}"
      sleep $delay
    done
  done
  printf "\r"
}

# File to store todo tasks
TODO_FILE="$HOME/.todo.txt"
touch $TODO_FILE

# Parse options with getopts
verbose=0
name=""
while getopts "vn:h" opt; do
  case $opt in
    v) verbose=1 ;;
    n) name=$OPTARG ;;
    h) usage ;;
    *) usage ;;
  esac
done
shift $((OPTIND -1))

# Subcommand routing
case "$1" in
  add)
    shift
    if [ -z "$1" ]; then
      error "Please provide a task to add."
      exit 1
    fi
    echo "[ ] $*" >> $TODO_FILE
    success "Added task: $*"
    ;;
  list)
    if [ ! -s $TODO_FILE ]; then
      info "No tasks found."
      exit 0
    fi
    nl -w2 -s'. ' $TODO_FILE
    ;;
  done)
    shift
    if ! [[ "$1" =~ ^[0-9]+$ ]]; then
      error "Please provide a valid task number."
      exit 1
    fi
    sed -i "${1}s/^\[ \]/[x]/" $TODO_FILE
    success "Marked task #$1 as done."
    ;;
  remove)
    shift
    if ! [[ "$1" =~ ^[0-9]+$ ]]; then
      error "Please provide a valid task number."
      exit 1
    fi
    sed -i "${1}d" $TODO_FILE
    success "Removed task #$1."
    ;;
  *)
    usage
    ;;
esac

Key Points

  • Use getopts in a while loop to parse command-line flags and access their values with OPTARG.
  • Shift positional parameters with shift $((OPTIND -1)) to handle subcommands and arguments after flags.
  • Enhance CLI output with ANSI escape codes for colors and define helper functions for consistent styling.
  • Create spinner animations by overwriting the same line with rotating characters to indicate progress.
  • Implement subcommand routing with a case statement and manage persistent data using plain text files and sed for edits.