Merge branch 'master' of ssh://git.uucp.hu/git/sysop/tools
[hband-tools.git] / bash / command-not-found-handle.sh
blobf2a8be8589741d9c2837f86481e1a34c2d15e9be
1 #!/bin/bash
3 command_not_found_handle()
5 local badcmd=$1
6 #shift
7 #declare -a args=("$@")
9 echo "$badcmd: command not found. similar commands: " >&2
11 local distance_threshold
12 if [ ${#badcmd} -le 4 ]
13 then
14 distance_threshold=1
15 else
16 distance_threshold=2
18 local startswith_threshold=3
19 local endswith_threshold=0
20 local min_suggestions=20
22 # 1. list every commands in all the PATH directories and bash functions
23 # and aliases.
24 # 2. measure Levenshtein between the typed word and each commands.
25 # 3. find exact matches of typed word in each commands and score them
26 # according to how close is it to the start or end of the command name.
27 # 4. calc the lowest score (the lower the better), ie. overall score.
28 # 5. sort the commands first by the lowest score, then length.
29 # 6. pass through commands, scores of which are below threshold.
30 # 7. worse-score commands are passed through too, in groups, to show at
31 # least a given number of suggestions.
32 # they are groupd by the overall score.
35 find ${PATH//:/ } -maxdepth 1 -type f -executable -printf '%f\n' 2>/dev/null
36 declare -p -F | cut -d' ' -f3-
37 alias -p | cut -d' ' -f2- | cut -d= -f1
39 | levenshtein-distance "$badcmd" \
40 | env badcmd=$badcmd perl -ne '
41 BEGIN {
42 use List::Util qw/min/;
43 $\ = "\n";
44 $, = "\t";
45 $worst_score = 999;
47 chomp;
48 s/^(\d+)\s//;
49 $distance = $1;
50 $len = length;
51 $spos = index $_, $ENV{badcmd}; $spos = $worst_score if $spos == -1;
52 $rpos = rindex $_, $ENV{badcmd};
53 if($rpos == -1) { $epos = $worst_score; } else { $epos = $len - ($rpos + length($ENV{badcmd})); }
54 print min($spos, $epos, $distance), $spos, $epos, $distance, $len, $_;' \
55 | sort -k1n -k5n \
56 | env min_suggestions=$min_suggestions startswith_threshold=$startswith_threshold endswith_threshold=$endswith_threshold distance_threshold=$distance_threshold\
57 perl -ne '
58 $, = "\t";
59 s/(\S+)\s(\S+)\s(\S+)\s(\S+)\s(\S+)\s//;
60 ($score, $spos, $epos, $distance, $len) = ($1, $2, $3, $4, $5);
61 if($spos > $ENV{startswith_threshold} and $epos > $ENV{endswith_threshold} and $distance > $ENV{distance_threshold})
63 if($printed >= $ENV{min_suggestions} and $score ne $last_score) { exit; }
65 print "$_";
66 $printed++;
67 $last_score = $score;' \
68 | column \
69 >&2
71 return 127