From Sole Partdridge, 2 Weeks ago, written in Plain Text.
  1. #!/bin/bash
  2. # Desc: Search shell history buy either, regexpr, date called, or working dir at time of call
  3. # Usage: hg [-adp] [-f FILE] [--cmd|--date|--location] REGEXP
  4. #          -a         search entire file, default is last 5000 entries
  5. #          -d         data only. print only matching table and feild. disables -p
  6. #          -f FILE    search a different file
  7. #          -p         pretty print
  8. #          +p         unpretty print
  9. #          --cmd      select cmd table as search target
  10. #          --date     select date table as search target
  11. #          --location select location table as search target
  12. #          --help     print this help message
  13. #
  14. #  Record Format                      Field Identifiers                 Field Values
  15. # ╭─────────────────────────────╮    ╭─────────────────────────────╮   ╭─────────────────────────────╮
  16. # │ ␍- cmd: to 'gawk'.␍         │ -> │\n\^ - cmd:\$\s              │ : │\s\^to 'gawk'.\$\n           │
  17. # │ ␉when: 1556828320␍          │ -> │\t\^when:\$\s                │ : │\s\^1556828320\$\n           │
  18. # │ ␉where: /home/mnrmnaugh/HWD␍│ -> │\t\^where:\$\s               │ : │\s\^/home/mnrmnaugh/HWD\$\n  │
  19. # ╰─────────────────────────────╯    ╰─────────────────────────────╯   ╰─────────────────────────────╯
  20. #
  21.  
  22.  
  23. # A function to properly format histfile. Should be called from PS1 or PROMPT_COMMAND
  24. #
  25. # histfmt(){
  26. #   histfile="$HOME"/.bash/data/HistMaster/histmaster
  27. #   history 1 | awk -v t="$(date +%s)" -v d="$(pwd)" '
  28. #     {$1=""; printf("\n- cmd:%s\n\twhen: %s\n\twhere: %s\n", $0, t, d)}' >> "$histfile"
  29. #   return $?
  30. #}
  31.  
  32.  
  33. # /* FUNCDEF */ --------------------------------------------------------------
  34. msg(){ printf -- "%s\n" "$@"; }
  35. emsg(){ printf -- "%s\n" "$@" >&2; }
  36. usage(){ awk -F '# ' 'NR>2{print $2};$0==""{exit}' <"$0"; }
  37. bail(){ emsg "$@"; usage; exit 1; }
  38.  
  39. histtok(){
  40.   # desc: print values for specified key
  41.   # usage: histtok cmd|when|where file
  42.   [[ -f "$2" ]] || return 1
  43.   [[ "$1" =~ (cmd|when|where) ]] || return 1
  44.   awk -v f="$1" -F': ' '$1~f{print $2}' <"$2"
  45. }
  46.  
  47. match(){
  48.   # desc: return matching field name and value
  49.   # usage: match regexpr file
  50.   # example: mapfile < <( match "string" "$(<./file)" )
  51.   local m
  52.   m="$1"; shift
  53.   printf -- "%s\n" "$@" \
  54.     | awk -v m="$m" '$0~m{print (NR-1)":"$0}'
  55. }
  56.  
  57. histgrep(){
  58.   # desc: create tables from histfile and perform search
  59.   # usage: histgrep pattern
  60.   declare table search file r
  61.   declare -a result index
  62.  
  63.   table="cmd"
  64.   [[ "$1" =~ (--cmd|--when|--where) ]] && table="${1#*--}" && shift 1 #while (($#)); do    case "$1" in      --cmd) table='cmd'; shift 1;;      --when) table='when'; shift 1;;      --where) table='where'; shift 1;;      *) search="$1"; file="$2"; shift 2;;    esac  done
  65.   search="$1" file="$2"
  66.   shift 2
  67.  
  68.   # Generate tables
  69.   mapfile -t cmd < <(histtok cmd "$file")
  70.   mapfile -t when < <(histtok when "$file")
  71.   mapfile -t where < <(histtok where "$file")
  72.  
  73.   # Perform search on selected table
  74.   case "$table" in
  75.     cmd) mapfile -t result < <(match "$search" "${cmd[@]}");;
  76.     when) mapfile -t result < <(match "$search" "${when[@]}");;
  77.     where) mapfile -t result < <(match "$search" "${where[@]}");;
  78.   esac
  79.  
  80.   # Clean up and align data points
  81.   for r in "${result[@]}"; do
  82.     index+=("${r%%:*}")
  83.   done
  84.  
  85.   # Print results
  86.   for r in "${index[@]}"; do
  87.     if [[ -z "$pretty" ]]; then
  88.       printf -- "- cmd: %s\n\twhen: %s\n\twhere: %s\n\n" "${cmd[$r]}" "${when[$r]}" "${where[$r]}"
  89.     else
  90.       tabsz="$(tabs -d 1|awk -F+ '$1~/^-+/{printf $1}'|wc -c)"
  91.       # NOTE: 7 is width of feild id i.e. '- cmd: '. No clue what 10 is...
  92.       let "(${#where[$r]}+7+tabsz > ${#cmd[$r]}+7+tabsz)" \
  93.         && let boxsz="(${#where[$r]}+10+tabsz)" \
  94.         || let boxsz="(${#cmd[$r]}+7+tabsz)"
  95.  
  96.       # Do It The Other Way (TM). vertical length? no! flatten it!
  97.       horz(){ eval printf $'\x1b\(0%c\x1b\(B' "q{1..${boxsz}"\}; }
  98.       printf -- "╭%s╮\n" "$(horz)"
  99.       #printf -- "│- cmd: %- $((boxsz-3-tabsz))s│\n│\twhen: %- $((boxsz-${#when[$r]}))s │\n│\twhere: %- $((boxsz-11))s │\n" "${cmd[$r]}" "${when[$r]}" "${where[$r]}"
  100.       printf -- "│- cmd: %- $((boxsz-3-tabsz))s│\n" "${cmd[$r]}"
  101.       printf -- "│\twhen: %- $((boxsz-${#when[$r]}))s │\n" "${when[$r]}"
  102.       printf -- "│\twhere: %- $((boxsz-11))s │\n" "${where[$r]}"
  103.       printf -- "╰%s╯\n" "$(horz)"
  104.     fi
  105.   done
  106. }
  107.  
  108. # /* ENV */ ------------------------------------------------------------------
  109. declare pattern file defaultfile tmp table pretty allscan dataonly
  110. pretty='true'
  111. defaultfile="$HOME/.bash/data/HistMaster/histmaster"
  112. table='--cmd'
  113.  
  114.  
  115. # /* INIT */ -----------------------------------------------------------------
  116. trap 'rm "$tmp" 2<&-' EXIT
  117. [[ -z "$1" ]] && bail "E!: search pattern required"
  118.  
  119.  
  120. # /* MAIN */ -----------------------------------------------------------------
  121. while (($#)); do
  122.   case "$1" in
  123.     --help) usage; exit 0;;
  124.     -p) pretty='true'; shift; continue;;
  125.     +p) pretty=''; shift; continue;;
  126.     -d) dataonly='true'; shift; continue;;
  127.     -f) [[ -r "$2" ]] || bail "E:! file not found"
  128.         file="${file:-$2}"; shift 2; continue;;
  129.     -a) allscan='true'; file="${file:-$defaultfile}"; shift; continue;;
  130.     --cmd) table="--cmd"; shift;;
  131.     --when|--date) table="--when"; shift;;
  132.     --where|--loc*) table="--where"; shift;;
  133.     *) [[ ! -r "$tmp" && ! "$allscan" == "true" ]] \
  134.          && tmp="./$(mktemp -u tmp.XXXXXXXXXX)" \
  135.          && tail -n 20000 "${file:-$defaultfile}" >"$tmp"
  136.       pattern="${pattern:-$1}" file="${file:-$tmp}"; shift; continue;;
  137.   esac
  138. done
  139.  
  140. if [[ -z "$dataonly" ]]; then
  141.   [[ -z "$pretty" ]] \
  142.   && histgrep "$table" "$pattern" "$file" \
  143.   || histgrep "$table" "$pattern" "$file" \
  144.     | awk -v pat="$pattern" '
  145.       {gsub(pat, "\x1b[1;97m&\x1b[0m", $0)}
  146.       {gsub(/(╭|╰)/, "\x1b[95m&", $0)}
  147.       {gsub(/│/, "│\x1b[92m", $0)}
  148.       {gsub(/│\x1b\[92m(\twh.*: |- cmd: )/, "&\x1b[0m", $0)}
  149.       {gsub(/│/, "\x1b[95m│", $0)}
  150.       {print $0}
  151.       '
  152. else
  153.   histgrep "$table" "$pattern" "$file" \
  154.     | awk -v tbl="$table" -F':' '$0~tbl{print $2}'
  155. fi
captcha