@@ -29,63 +29,81 @@ function __hhs_history() {
2929 return $?
3030}
3131
32- # inspiRED by https://superuser.com/questions/250227/how-do-i-see-what-my-most-used-linux-command-are
33- # @function: Display statistics about commands in history.
32+ # @function: Display statistics about commands in history (robust date/time filtering)
3433# @param $1 [Opt] : Limit to the top N commands.
3534function __hhs_hist_stats() {
36-
37- local top_n= ${1 :- 10} width= ${2 :- 30} i=1 cmd_name cmd_qty hist_output bar_len bar columns pad_len pad
35+ local top_n= ${1 :- 10} width= ${2 :- 30} i=1
36+ local cmd_name cmd_qty hist_output bar_len bar columns pad_len pad max_size
3837
3938 if [[ " $1 " == " -h" || " $1 " == " --help" ]]; then
4039 echo " usage: ${FUNCNAME[0]} [top_N]"
41- return 1
40+ return 0
4241 fi
4342
44- hist_output=" $( history | tr -s ' ' | cut -d ' ' -f6 | sort | uniq -c | sort -nr) "
43+ # Generic parser: remove metadata, extract first non-date token as command
44+ hist_output=" $(
45+ history |
46+ sed -E ' s/^\[[^]]*\][[:space:]]*//' | # remove [user,date,...]
47+ sed -E ' s/^[[:space:]]*[0-9]+\**[[:space:]]*//' | # remove numeric ids
48+ awk ' {
49+ for (i=1; i<=NF; i++) {
50+ token=$i
51+ # Skip timestamps and dates (e.g., 2025-11-05, 23:47:12, etc.)
52+ if (token ~ /^[0-9]{4}-[0-9]{2}-[0-9]{2}$/) continue
53+ if (token ~ /^[0-9]{2}:[0-9]{2}:[0-9]{2}$/) continue
54+ if (token ~ /^([0-9]{4}-[0-9]{2}-[0-9]{2})[T ]([0-9]{2}:[0-9]{2}:[0-9]{2})$/) continue
55+ if (token ~ /^[0-9]{1,4}$/) continue
56+ if (token ~ /^[[:punct:]]+$/) continue
57+ if (token ~ /^[[:alnum:]_.\/:+-]+$/) { CMD[token]++; break }
58+ }
59+ }
60+ END { for (c in CMD) print CMD[c], c }' |
61+ sort -nr
62+ ) "
63+
64+ [[ -z " ${hist_output} " ]] && {
65+ __hhs_errcho " ${FUNCNAME[0]} " " No valid command tokens found in history."
66+ return 1
67+ }
68+
69+ columns=$( tput cols 2> /dev/null || echo 80)
70+ pad_len=$(( columns / 2 ))
71+ pad=$( printf ' %0.1s' " ." {1..120})
4572
46- columns=$( tput cols)
47- pad=$( printf ' %0.1s' " ." {1..41})
48- pad_len=41
4973 max_size=$( echo " ${hist_output} " | head -n 1 | awk ' {print $1}' )
74+ [[ -z " ${max_size} " || " ${max_size} " -le 0 ]] && max_size=1
5075
51- echo ' '
52- echo " ${YELLOW} Top ${top_n} used commands in history:"
53- echo ' '
76+ echo ' '
77+ echo " ${YELLOW} Top ${top_n} used commands in history:${NC} "
78+ echo ' '
5479
55- hist_output=" $( echo " ${hist_output} " | head -n " ${top_n} " ) "
80+ while read -r cmd_qty cmd_name; do
81+ [[ -z " ${cmd_qty} " || -z " ${cmd_name} " ]] && continue
5682
57- echo " ${hist_output} " | while read -r line ; do
58- if [[ " ${line} " =~ ^[[:space:]] * ([0-9]+)[[:space:]] * ([a-zA-Z0-9]+)$ ]] ; then
83+ bar_len= $(( (cmd_qty * width) / max_size ))
84+ (( bar_len < 1 )) && bar_len=1
5985
60- cmd_qty=" ${BASH_REMATCH[1]} "
61- cmd_name=" ${BASH_REMATCH[2]} "
62- cmd_name=" ${cmd_name: 0: $columns } "
86+ if __hhs_has seq; then
87+ bar=$( printf ' ▄%.0s' $( seq 1 " ${bar_len} " ) )
88+ elif __hhs_has jot; then
89+ bar=$( printf ' ▄%.0s' " $( jot - 1 " ${bar_len} " ) " )
90+ else
91+ __hhs_errcho " ${FUNCNAME[0]} " " Neither seq nor jot is available."
92+ return 1
93+ fi
6394
64- bar_len=$(( (cmd_qty * width) / max_size))
65- [[ $bar_len -gt 0 && $bar_len -lt 2 ]] && bar_len=2
95+ [[ ${# cmd_name} -gt $(( columns - 20 )) ]] && cmd_name=" ${cmd_name: 0: $((columns - 20))} …"
6696
67- if __hhs_has seq; then
68- bar=$( printf ' ▄%.0s' $( seq 1 " ${bar_len} " ) )
69- elif __hhs_has jot; then
70- bar=$( printf ' ▄%.0s' " $( jot - 1 " ${bar_len} " ) " )
71- else
72- __hhs_errcho " ${FUNCNAME[0]} " " Neither seq nor jot is available."
73- return 1
74- fi
97+ printf " ${WHITE} %3d: ${HHS_HIGHLIGHT_COLOR} " " ${i} "
98+ printf " %s" " ${cmd_name} "
99+ printf ' %*.*s' 0 $(( pad_len - ${# cmd_name} )) " ${pad} "
100+ printf " ${GREEN} %4d ${ORANGE} |%s${NC} \n" " ${cmd_qty} " " ${bar} "
75101
76- printf " ${WHITE} %3d: ${HHS_HIGHLIGHT_COLOR} " " ${i} "
77- printf " %s " " ${cmd_name} "
78- [[ " ${# cmd_name} " -ge " ${columns} " ]] && echo -en " ${NC} " || echo -en " ${NC} "
79- printf ' %*.*s' 0 $(( pad_len - ${# cmd_name} )) " ${pad} "
80- printf " ${GREEN} %3d ${ORANGE} |%s\n" " ${cmd_qty} " " ${bar} "
81- (( i += 1 ))
82- fi
83- done
102+ (( i += 1 ))
103+ done < <( echo " ${hist_output} " | head -n " ${top_n} " )
84104
85- IFS= " ${OLDIFS} "
105+ echo ' '
86106 echo " ${NC} "
87-
88- return 0
89107}
90108
91109# @function: Display the current dir (pwd) and remote repo url, if it applies.
@@ -235,75 +253,115 @@ function __hhs_shopt() {
235253 return 1
236254}
237255
238- # @function: Display 'du' output formatted as a horizontal bar chart.
256+ # @function: Display 'du' output formatted as a horizontal bar chart (auto unit scaling) .
239257# @param $1 [Opt] : Directory path (default: current directory)
240258# @param $2 [Opt] : Number of top entries to display (default: 10)
259+ # @param $3 [Opt] : Chart bar width scaling factor (default: 30)
241260function __hhs_du() {
242- local dir=" ${1:- .} " i=1 top_n=${2:- 10} width=${3:- 30} max_size du_output columns pad_len pad bar_len bar path
261+ local dir=" ${1:- .} "
262+ local top_n=${2:- 10}
263+ local width=${3:- 30}
264+ local i=1 du_output term_w pad_len pad max_val bar_len bar path entry_count
265+ local size_human size_kib
243266
244267 if [[ " $1 " == " -h" || " $1 " == " --help" ]]; then
245- echo " usage: ${FUNCNAME[0]} [path] [top_N]"
246- return 1
268+ echo " usage: ${FUNCNAME[0]} [path] [top_N] [width] "
269+ return 0
247270 fi
248271
249272 [[ ! -d " ${dir} " ]] && {
250273 __hhs_errcho " ${FUNCNAME[0]} " " Directory not found: \" ${dir} \" "
251274 return 1
252275 }
253276
277+ # Collect du output (human readable)
254278 if [[ " $( uname -s) " == " Darwin" ]]; then
255- du_output=" $( \d u -hkd 1 " ${dir} " 2> /dev/null | sort -rn | head -n " $(( top_n + 1 )) " ) "
279+ du_output=" $( \d u -hd 1 " ${dir} " 2> /dev/null | grep -v ' total ' | sort -hr ) "
256280 else
257- du_output=" $( \d u -hk --max-depth=1 " ${dir} " 2> /dev/null | sort -rn | head -n " $(( top_n + 1 )) " ) "
281+ du_output=" $( \d u -h --max-depth=1 " ${dir} " 2> /dev/null | grep -v ' total ' | sort -hr ) "
258282 fi
259283
260- columns=$( tput cols)
261- pad_len=60
262- pad=$( printf ' %0.1s' " ." {1..60})
263- max_size=$( echo " ${du_output} " | awk ' {print $1}' | sort -n | awk ' {
264- a[NR]=$1
265- } END {
266- idx = int(NR * 0.9)
267- if (idx < 1) idx = 1
268- print a[idx]
269- }' )
270-
271- echo ' '
272- echo " ${YELLOW} Top ${top_n} disk usage at: ${BLUE} \" ${dir// \. / $(pwd)} \" "
273- echo ' '
274-
275- while read -r size path; do
276- size=$( echo " ${size} " | tr -d ' [:space:]' )
277- [[ -z " ${size} " || -z " ${path} " || " ${path} " == ' .' || " ${path} " == ' total' ]] && continue
278- bar_len=$(( (size * width) / max_size))
279- [[ $bar_len -gt 0 && $bar_len -lt 2 ]] && bar_len=2
280-
281- if [[ $bar_len -gt 0 ]]; then
282- if __hhs_has seq; then
283- bar=$( printf ' ▄%.0s' $( seq 1 " ${bar_len} " ) )
284- elif __hhs_has jot; then
285- bar=$( printf ' ▄%.0s' " $( jot - 1 " ${bar_len} " ) " )
286- else
287- __hhs_errcho " ${FUNCNAME[0]} " " Neither seq nor jot is available."
288- return 1
289- fi
284+ entry_count=$( echo " ${du_output} " | wc -l | tr -d ' [:space:]' )
285+ [[ " ${entry_count} " -eq 0 ]] && {
286+ __hhs_errcho " ${FUNCNAME[0]} " " No usable entries found in: \" ${dir} \" "
287+ return 1
288+ }
289+
290+ du_output=$( echo " ${du_output} " | head -n " ${top_n} " )
291+
292+ term_w=$( tput cols 2> /dev/null || echo 80)
293+ (( term_w < 40 )) && term_w=80
294+ pad_len=$(( term_w / 2 ))
295+ pad=$( printf ' %0.1s' " ." {1..120})
296+
297+ # Compute max size in KiB for bar scaling
298+ max_val=$( echo " ${du_output} " | awk '
299+ function to_kib(v) {
300+ unit=tolower(substr(v, length(v)))
301+ val=substr(v, 1, length(v)-1)
302+ if (unit=="g") return int(val*1024*1024)
303+ if (unit=="m") return int(val*1024)
304+ if (unit=="k") return int(val)
305+ if (unit=="b") return int(val/1024)
306+ return int(v)
307+ }
308+ {print to_kib($1)}' | sort -n | tail -1)
309+ [[ -z " ${max_val} " || " ${max_val} " -le 0 ]] && max_val=1
310+
311+ echo ' '
312+ echo " ${YELLOW} Top ${top_n} disk usage at: ${BLUE} \" ${dir// \. / $(pwd)} \" ${NC} "
313+ echo ' '
314+
315+ while read -r size_human path; do
316+ [[ -z " ${size_human} " || -z " ${path} " ]] && continue
317+
318+ # Convert to KiB for scaling
319+ size_kib=$( awk -v v=" ${size_human} " '
320+ function to_kib(x) {
321+ unit=tolower(substr(x, length(x)))
322+ val=substr(x, 1, length(x)-1)
323+ if (unit=="g") return int(val*1024*1024)
324+ if (unit=="m") return int(val*1024)
325+ if (unit=="k") return int(val)
326+ if (unit=="b") return int(val/1024)
327+ return int(x)
328+ }
329+ BEGIN { print to_kib(v) }' )
330+
331+ # Normalize path
332+ if [[ " ${path} " == ' .' ]]; then
333+ path=" ./$( basename " ${dir} " ) "
334+ else
335+ path=" ${path// \.\/ / } "
336+ path=" ${path// \/\/ / \/ } "
337+ fi
338+
339+ bar_len=$(( (size_kib * width) / max_val))
340+ (( bar_len < 1 )) && bar_len=1
341+
342+ if __hhs_has seq; then
343+ bar=$( printf ' ▄%.0s' $( seq 1 " ${bar_len} " ) )
344+ elif __hhs_has jot; then
345+ bar=$( printf ' ▄%.0s' " $( jot - 1 " ${bar_len} " ) " )
346+ else
347+ __hhs_errcho " ${FUNCNAME[0]} " " Neither seq nor jot is available."
348+ return 1
290349 fi
291350
292- path=" ${path// \.\/ / } "
293- path=" ${path// \/\/ / \/ } "
294- path=" ${path: 0: $columns } "
351+ # Safe truncation for narrow terminals
352+ local max_label_width=$(( term_w - 30 ))
353+ (( max_label_width < 10 )) && max_label_width=10
354+ [[ ${# path} -gt ${max_label_width} ]] && path=" ${path: 0: ${max_label_width} } …"
295355
296356 printf " ${WHITE} %3d: ${HHS_HIGHLIGHT_COLOR} " " ${i} "
297357 printf " %s" " ${path} "
298358 printf ' %*.*s' 0 $(( pad_len - ${# path} )) " ${pad} "
299- [[ " ${# path} " -ge " ${columns} " ]] && echo -en " ${NC } " || echo -en " ${NC } "
300- printf " ${GREEN} % $(( ${ # max_size} + 1 )) d KB ${ORANGE} |%s \n " " ${size} " " ${bar} "
359+ printf " ${GREEN} %8s ${ORANGE} |%s ${NC} \n " " ${size_human } " " ${bar } "
360+
301361 (( i += 1 ))
302362 done <<< " ${du_output}"
303363
304364 echo ' '
305- echo " ${WHITE} Total: ${ORANGE} $( \d u -hc " ${dir} " | grep total | cut -f1) "
306- unset TOTAL
307-
308- echo " ${NC} "
365+ echo " ${WHITE} Total: ${ORANGE} $( \d u -sh " ${dir} " 2> /dev/null | awk ' {print $1}' ) ${NC} "
366+ echo ' '
309367}
0 commit comments