#!/bin/bash set -euo pipefail if [ "$#" == "0" ] then echo recall exit 0 fi . "${BASH_SOURCE[0]%/*}"/helpers/common.sh stderr_deps=/dev/null # NOTE: uncomment to see err output from `which` (if any) #stderr_deps='/proc/self/fd/2' check_deps 3>&2 2>"$stderr_deps" 1>/dev/null LOG_ROOT="$HOME"/.local/var/log/shell # source this EXIT_SUCCESS=false LIST_MODE=false CMD_STRING=false ORS='\n' usage () { cat < 0 )) then NUM="$OPTARG" else echo "-n paremeter must be an integer greater than zero" >&2 echo >&2 usage >&2 exit 1 fi ;; z) EXIT_SUCCESS=true ;; s) CMD_STRING=true ;; 0) ORS='\0' ;; \?) echo "Unknown switch: -$OPTARG" >&2 echo >&2 usage >&2 exit 1 ;; esac } validate () { # exit if vars are not valid # ie, set any dynamic defaults here if ! "$LIST_MODE" && [ "${NUM:=1}" ] && ! (( NUM == 1 )) then echo "-n != 1 can only be specified with -l" >&2 echo >&2 usage >&2 exit 1 fi if ! "$LIST_MODE" && [ "$#" == "0" ] then echo "PROG must be provided" >&2 echo >&2 usage >&2 exit 1 fi } while getopts "${OPTSTRING}" opt do parse_opt done shift $((OPTIND-1)) validate "$@" fzf_rw0_inplace_preview () { # NOTE: to preview and select captured command directories header_len="$1" # shellcheck disable=SC2016 fzf \ --read0 --print0 \ -m \ --no-sort \ -d / \ --preview-window="~$header_len" \ --preview='cat {}/info; head -n "$LINES" {}/dat' \ && : } awk_strip_first_path_part_script () { cat <<'EOF' { for (i=2; i<=NF; i++) printf "%s%s", $(i), (i a/b/c (null delimited) awk \ -v FS='/' -v RS='\0' \ -v OFS='/' -v ORS='\0' \ "$(awk_strip_first_path_part_script)" \ && : } awk_r0_add_prefix () { # NOTE: some-string -> some-string (null delimited) : "${2?awk_r0_add_prefix requires exactly two positional args}" : "${1:?awk_r0_add_prefix output record separator must be set and non-empty}" [ "${1:+DefinedNotEmpty}" == "DefinedNotEmpty" ] [ "${2+DefinedMaybeEmpty}" == "DefinedMaybeEmpty" ] awk \ -v prefix="$2/" \ -v RS='\0' \ -v ORS="$1" \ '{print prefix $0}' \ && : } awk_r0_add_suffix () { # NOTE: some-string -> some-string (null delimited) : "${2?awk_r0_add_suffix requires exactly two positional args}" : "${1:?awk_r0_add_suffix output record separator must be set and non-empty}" [ "${1:+DefinedNotEmpty}" == "DefinedNotEmpty" ] [ "${2+DefinedMaybeEmpty}" == "DefinedMaybeEmpty" ] awk \ -v suffix="/$2" \ -v RS='\0' \ -v ORS="$1" \ '{print $0 suffix}' \ && : } sort_rw0 () { # NOTE: sort by program, or timestamp if [ "$1" = "prog" ] then sort -zrV elif [ "$1" = "ts" ] then awk \ -v FS='/' -v RS='\0' \ -v OFS='/' -v ORS='\0' \ '{print $NF,$0}' \ | sort -zrV \ | awk_rw0_strip_first_path_part \ : else echo "unsupported sort option: $1" >&2 false fi } list_r0 (){ ors="$1" if ! "$CMD_STRING" then awk_rw0_strip_first_path_part \ | awk_r0_add_prefix "$ors" "$LOG_ROOT" \ && : else awk_r0_add_suffix '\0' 'info' \ | xargs -r0n1 head -n1 \ | awk \ -v RS='\n' \ -v ORS="$ors" \ '{print $0}' \ && : fi } if "$LIST_MODE" then # TODO: make sort option configurable sort_opt="ts" find_name=() if [ "${1:-}" ] then slugs=( "$1" ) if [ "${2:-}" ] then slugs+=( "$2" ) fi query="$(bash -ac 'IFS=/; echo "$*"' "recall-bash" "${slugs[@]}")" find_name=( -wholename "**/$query/**" ) fi ( cd "$LOG_ROOT" find . \ \( -type l -o -type d \) -wholename './????????_??????_?????????' -prune -o -type d "${find_name[@]}" \( \ -links 2 \ -o \( \ -links 1 -exec bash -c '! [ "$(find "$1" -mindepth 1 -type d)" ]' find-bash {} \; \ \) \ \) \ -print0 \ | sort_rw0 "$sort_opt" \ | head -zn "${NUM:--0}" \ | fzf_rw0_inplace_preview "2" \ | list_r0 "$ORS" \ && : ) else PROG="$1" SUBPROG="$(get_subprog "$@")" LOG_DIR="$LOG_ROOT"/"$PROG"/"$SUBPROG" LOG_DIR="$LOG_DIR"/"$(cd "$LOG_DIR" && find . -mindepth 1 -maxdepth 1 -print0 | sort -zV | tail -zn1 | xargs -r -0 echo)" OUT="$LOG_DIR"/stdout ERR="$LOG_DIR"/stderr # shellcheck disable=SC2034 DAT="$LOG_DIR"/dat INF="$LOG_DIR"/info # shellcheck disable=SC1090 . <(tail -n +2 "$INF") # first line is commandline "$EXIT_SUCCESS" && status=0 #TODO: allow user to request stderr in case it is needed for # testing redirections # disabled by defualt as it won't be interleaved correctly # on the console false && cat "$ERR" >&2 cat "$OUT" exit "$status" fi