Created
July 18, 2025 17:45
-
-
Save genedelisa/407d3d3783d0092f43226e9af7aa5e10 to your computer and use it in GitHub Desktop.
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
| #!/usr/bin/env zsh | |
| # -*- mode: sh; sh-shell: zsh; sh-indentation: 4; sh-basic-offset: 4; coding: utf-8; -*- | |
| # vim: ft=zsh:sw=4:ts=4:et | |
| # | |
| # Time-stamp: "Last Modified 2025-07-18 13:43:00 by Gene De Lisa, genedelisa" | |
| # | |
| # File: mkcatalog | |
| # | |
| # Generate a list of filenames on External drives | |
| # | |
| # Gene De Lisa | |
| # [email protected] | |
| # http://rockhoppertech.com/blog/ | |
| # License - http://unlicense.org | |
| ################################################################################ | |
| # https://google.github.io/styleguide/shellguide.html#s7-naming-conventions | |
| # https://zsh.sourceforge.io/Doc/Release/Functions.html | |
| # https://zsh.sourceforge.io/Doc/Release/Options.html | |
| ################################################################################ | |
| # _ _ _ | |
| # _ __ ___ | | _____ __ _| |_ __ _| | ___ __ _ | |
| # | '_ ` _ \| |/ / __/ _` | __/ _` | |/ _ \ / _` | | |
| # | | | | | | < (_| (_| | || (_| | | (_) | (_| | | |
| # |_| |_| |_|_|\_\___\__,_|\__\__,_|_|\___/ \__, | | |
| # |___/ | |
| # | |
| ################################################################################ | |
| # -R reset zsh options | |
| # -L options LOCAL_OPTIONS, LOCAL_PATTERNS and LOCAL_TRAPS will be set | |
| emulate -LR zsh | |
| # Enable nullglob to avoid errors if no files match | |
| setopt nullglob | |
| readonly local SCRIPT_NAME=${ZSH_SCRIPT:t:r} | |
| readonly local logo=$(print -- $SCRIPT_NAME | figlet -c -f basic) | |
| typeset -r version=0.1.0 | |
| ################################################################################ | |
| autoload -Uz zsh-color-messages && zsh-color-messages | |
| local loglevel=error | |
| local use_files | |
| local use_directories | |
| local do_sort | |
| local volume | |
| local maxdepth=1 | |
| local destdir=~/External\ Drive\ Catalogs | |
| local now=$(date +'%Y-%m-%d-%H-%M-%S') | |
| local usage_text=""" | |
| Specify directories and/or files | |
| ${SCRIPT_NAME} [-d | --directories] [-f | --files] [-s | --sort] [--volume vol] [--maxdepth n] | |
| -h --help | |
| -v --verbose | |
| -d --directories | |
| -f --files | |
| -s --sort | |
| --volume vol | |
| --maxdepth n | |
| --destdir path | |
| ${SCRIPT_NAME} -d -s | |
| ${SCRIPT_NAME} -f -s | |
| ${SCRIPT_NAME} -d -f -s | |
| ${SCRIPT_NAME} -d -f -s -m 2 | |
| ${SCRIPT_NAME} -dfsm 2 | |
| ${SCRIPT_NAME} --directories --files --sort --maxdepth 2 | |
| ${SCRIPT_NAME} -d -f -s --volume /Volumes/WDElements5TB | |
| ${SCRIPT_NAME} --directories --files --sort --maxdepth 2 --destdir $HOME/catalogs | |
| ${SCRIPT_NAME} -d -f -s --maxdepth 2 --volume /Volumes/WDElements5TB | |
| ${SCRIPT_NAME} -dfsm 2 --volume /Volumes/WDElements5TB | |
| """ | |
| ############################################################################### | |
| parse_commandline() { | |
| local -A local_options | |
| zparseopts -a local_options -D -E -F -- \ | |
| h=help_opt -help=help_opt \ | |
| v=verbose_opt -verbose=verbose_opt \ | |
| -volume:=volume_opt \ | |
| -destdir:=destdir_opt \ | |
| m:=maxdepth_opt -maxdepth:=maxdepth_opt \ | |
| f=files_opt -files=files_opt \ | |
| d=directories_opt -directories=directories_opt \ | |
| s=sort_opt -sort=sort_opt \ | |
| || { | |
| >&2 print -- "zparseopts error" ${status} | |
| >&2 print -- "zparseopts error" ${local_options} | |
| return 1 | |
| } | |
| # the -D flag removed the options | |
| # now it's an array | |
| non_flag_args="$@" | |
| nonflag_args_array=("$@") | |
| [[ -n ${help_opt} ]] && { | |
| clear | |
| print -rC1 -- $logo | |
| print -rC1 -- $usage_text | |
| exit 0 | |
| } | |
| [[ -n ${verbose_opt} ]] && { | |
| ZSH_DEBUG_PRINT=t | |
| loglevel=info | |
| } | |
| [[ -n ${files_opt} ]] && { | |
| use_files=t | |
| zsh_debug_message "use_files $use_files" | |
| } | |
| [[ -n ${directories_opt} ]] && { | |
| use_directories=t | |
| zsh_debug_message "use_directories $use_directories" | |
| } | |
| [[ -n ${sort_opt} ]] && { | |
| sort=t | |
| zsh_debug_message "sort" | |
| } | |
| [[ ${#volume_opt} -ge 2 ]] && { | |
| volume=${${volume_opt:+"${volume_opt[2]/#=/}"}} | |
| zsh_debug_message "volume $volume" | |
| } | |
| [[ ${#maxdepth_opt} -ge 2 ]] && { | |
| maxdepth=${${maxdepth_opt:+"${maxdepth_opt[2]/#=/}"}} | |
| zsh_debug_message "maxdepth $maxdepth" | |
| } | |
| [[ ${#destdir_opt} -ge 2 ]] && { | |
| destdir=${${destdir_opt:+"${destdir_opt[2]/#=/}"}} | |
| zsh_debug_message "destdir $destdir" | |
| } | |
| return 0 | |
| } | |
| ############################################################################### | |
| choose_volume() { | |
| print -- "choosing volume" | |
| local -a volumes=($(print -- /Volumes/*(N/) 2>/dev/null )) | |
| local -a selected_files=() | |
| while IFS= read -r -d '' file | |
| do | |
| selected_files+=("$file") | |
| done < <(printf '%s\0' "${volumes[@]}" | \ | |
| fzf --read0 --print0 \ | |
| --bind "esc:abort" \ | |
| --bind 'ctrl-v:toggle-preview' \ | |
| --bind 'ctrl-y:preview-up' \ | |
| --bind 'ctrl-e:preview-down' \ | |
| --bind 'ctrl-b:preview-page-up' \ | |
| --bind 'ctrl-f:preview-page-down' \ | |
| --bind 'ctrl-j:preview-half-page-up' \ | |
| --bind 'ctrl-k:preview-half-page-down' \ | |
| --bind 'shift-up:preview-top' \ | |
| --bind 'shift-down:preview-bottom' \ | |
| --bind 'alt-up:half-page-up' \ | |
| --bind 'alt-down:half-page-down' \ | |
| --header='ctrl-a: select all ctrl-d: deselect all \ | |
| enter: open selected escape: abort' \ | |
| --ansi \ | |
| --no-sort \ | |
| --margin='1,2,1,2' \ | |
| --preview 'fzf-gd-preview {}' | |
| ) | |
| volume=$selected_files | |
| zsh_debug_message "selected volume $volume" | |
| zsh_debug_message "use_directories $use_directories" | |
| return 0 | |
| } | |
| ############################################################################### | |
| process() { | |
| # the pruning is to not print | |
| # /Volumes/WDElements5TB/.Spotlight-V100 | |
| # /Volumes/WDElements5TB/.fseventsd | |
| [[ -n ${use_directories} ]] && { | |
| print -- just directories | |
| local destfile=$destdir/${volume:t}.dirs.$maxdepth.$now.txt | |
| zsh_debug_message "Writing to $destfile" | |
| print -n -- $destfile | pbcopy | |
| command date --iso-8601=seconds > $destfile | |
| command df -h $volume >> $destfile | |
| [[ -n ${sort} ]] && { | |
| command find $volume -maxdepth $maxdepth -type d \ | |
| \( -path '**/.Spotlight-V100' -o -path '**/.fseventsd' \) -prune -o -print 2>/dev/null \ | |
| | LC_ALL=C sort --ignore-case >> $destfile | |
| } || { | |
| command find $volume -maxdepth $maxdepth -type d \ | |
| \( -path '**/.Spotlight-V100' -o -path '**/.fseventsd' \) -prune -o -print 2>/dev/null >> $destfile | |
| } | |
| } | |
| [[ -n ${use_files} ]] && { | |
| zsh_debug_message "all files" | |
| local destfile=$destdir/${volume:t}.files.$maxdepth.$now.txt | |
| zsh_debug_message "Writing to $destfile" | |
| print -n -- $destfile | pbcopy | |
| command date --iso-8601=seconds > $destfile | |
| command df -h $volume >> $destfile | |
| [[ -n ${sort} ]] && { | |
| command find $volume -maxdepth $maxdepth \ | |
| \( -path '**/.Spotlight-V100' -o -path '**/.fseventsd' \) -prune -o -print 2>/dev/null \ | |
| | LC_ALL=C sort --ignore-case >> $destfile | |
| } || { | |
| command find $volume -maxdepth $maxdepth \ | |
| \( -path '**/.Spotlight-V100' -o -path '**/.fseventsd' \) -prune -o -print 2>/dev/null >> $destfile | |
| } | |
| } | |
| return 0 | |
| } | |
| ################ main flow | |
| parse_commandline "${@}" | |
| [[ ${status} -ne 0 ]] && { | |
| >&2 print -- "Oops. zparseopts error" ${status} | |
| exit $status | |
| } | |
| [[ -z ${use_directories} && -z ${use_files} ]] && { | |
| >&2 print -- $usage_text | |
| exit 2 | |
| } | |
| [[ -n ${volume} ]] && { | |
| [[ ${loglevel} == "info" ]] && zsh_debug_message "volume is set from flag: ${volume}" | |
| } | |
| [[ -z ${volume} ]] && { | |
| choose_volume | |
| } | |
| [[ ${loglevel} == "info" ]] && { | |
| zsh_debug_message "selected volume $volume" | |
| zsh_debug_message "use_directories $use_directories" | |
| zsh_debug_message "use_files $use_files" | |
| zsh_debug_message "sort $sort" | |
| zsh_debug_message "maxdepth $maxdepth" | |
| zsh_debug_message "destdir $destdir" | |
| } | |
| process | |
| exit $status |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment