]>
Commit | Line | Data |
---|---|---|
28ef6c31 JA |
1 | ##### |
2 | #To: chet@po.cwru.edu, sarahmckenna@lucent.com | |
3 | #Message-Id: <slrn8mqioc.msb.ian@lovelorn.linuxcare.com> | |
4 | #Posted-To: comp.unix.shell, gnu.bash.bug | |
5 | #Subject: bash 2.04 programmable completion examples | |
6 | #Reply-To: ian@linuxcare.com, ian@caliban.org | |
7 | #Summary: examples of programmable completion for bash 2.04 | |
8 | #Date: Thu, 13 Jul 2000 00:52:33 -0400 (EDT) | |
9 | #From: ianmacd@linuxcare.com (Ian Macdonald) | |
10 | ##### | |
11 | ||
12 | ######################################################################### | |
13 | # Turn on extended globbing | |
14 | shopt -s extglob | |
15 | ||
16 | # A lot of the following one-liners were taken directly from the | |
17 | # completion examples provided with the bash 2.04 source distribution | |
18 | ||
19 | # Make directory commands see only directories | |
20 | complete -d cd mkdir rmdir pushd | |
21 | ||
22 | # Make file commands see only files | |
23 | complete -f cat less more chown ln strip | |
24 | complete -f -X '*.gz' gzip | |
25 | complete -f -X '*.Z' compress | |
26 | complete -f -X '!*.+(Z|gz|tgz|Gz)' gunzip zcat zmore | |
27 | complete -f -X '!*.Z' uncompress zmore zcat | |
28 | complete -f -X '!*.+(gif|jpg|jpeg|GIF|JPG|bmp)' ee xv | |
29 | complete -f -X '!*.+(ps|PS|ps.gz)' gv | |
30 | complete -f -X '!*.+(dvi|DVI)' dvips xdvi dviselect dvitype | |
31 | complete -f -X '!*.+(pdf|PDF)' acroread xpdf | |
32 | complete -f -X '!*.texi*' makeinfo texi2dvi texi2html | |
33 | complete -f -X '!*.+(tex|TEX)' tex latex slitex | |
34 | complete -f -X '!*.+(mp3|MP3)' mpg123 | |
35 | ||
36 | # kill sees only signals | |
37 | complete -A signal kill -P '%' | |
38 | ||
39 | # user commands see only users | |
40 | complete -u finger su usermod userdel passwd | |
41 | ||
42 | # bg completes with stopped jobs | |
43 | complete -A stopped -P '%' bg | |
44 | ||
45 | # other job commands | |
46 | complete -j -P '%' fg jobs disown | |
47 | ||
48 | # network commands complete with hostname | |
49 | complete -A hostname ssh rsh telnet rlogin ftp ping fping host traceroute \ | |
50 | nslookup | |
51 | ||
52 | # export and others complete with shell variables | |
53 | complete -v export local readonly unset | |
54 | ||
55 | # set completes with set options | |
56 | complete -A setopt set | |
57 | ||
58 | # shopt completes with shopt options | |
59 | complete -A shopt shopt | |
60 | ||
61 | # helptopics | |
62 | complete -A helptopic help | |
63 | ||
64 | # unalias completes with aliases | |
65 | complete -a unalias | |
66 | ||
67 | # various commands complete with commands | |
68 | complete -c command type nohup exec nice eval strace gdb | |
69 | ||
70 | # bind completes with readline bindings (make this more intelligent) | |
71 | complete -A binding bind | |
72 | ||
73 | # Now we get to the meat of the file, the functions themselves. Some | |
74 | # of these are works in progress. Most assume GNU versions of the | |
75 | # tools in question and may require modifications for use on vanilla | |
76 | # UNIX systems. | |
77 | # | |
78 | # A couple of functions may have non-portable, Linux specific code in | |
79 | # them, but this will be noted where applicable | |
80 | ||
81 | ||
82 | # GNU chown(1) completion. This should be expanded to allow the use of | |
83 | # ':' as well as '.' as the user.group separator. | |
84 | # | |
85 | _chown () | |
86 | { | |
87 | local cur prev user group | |
88 | ||
89 | COMPREPLY=() | |
90 | cur=${COMP_WORDS[COMP_CWORD]} | |
91 | prev=${COMP_WORDS[COMP_CWORD-1]} | |
92 | ||
93 | # do not attempt completion if we're specifying an option | |
94 | if [ "${cur:0:1}" = "-" ]; then return 0; fi | |
95 | ||
96 | # first parameter on line or first since an option? | |
97 | if [ $COMP_CWORD -eq 1 ] || [ "${prev:0:1}" = "-" ]; then | |
98 | case "$cur" in | |
99 | [a-zA-Z]*.*) | |
100 | user=${cur%.*} | |
101 | group=${cur#*.} | |
102 | COMPREPLY=( $( awk 'BEGIN {FS=":"} \ | |
103 | {if ($1 ~ /^'$group'/) print $1}' \ | |
104 | /etc/group ) ) | |
105 | for (( i=0; i < ${#COMPREPLY[@]}; i++ )); do | |
106 | COMPREPLY[i]=$user.${COMPREPLY[i]} | |
107 | done | |
108 | return 0 | |
109 | ;; | |
110 | *) | |
111 | COMPREPLY=( $( compgen -u $cur -S '.' ) ) | |
112 | return 0 | |
113 | ;; | |
114 | esac | |
115 | else | |
116 | COMPREPLY=( $( compgen -f $cur ) ) | |
117 | fi | |
118 | ||
119 | return 0 | |
120 | } | |
121 | complete -F _chown chown | |
122 | ||
123 | # umount(8) completion. This relies on the mount point being the third | |
124 | # space-delimited field in the output of mount(8) | |
125 | # | |
126 | _umount () | |
127 | { | |
128 | local cur | |
129 | ||
130 | COMPREPLY=() | |
131 | cur=${COMP_WORDS[COMP_CWORD]} | |
132 | ||
133 | # could rewrite the cut | grep to be a sed command, but this is | |
134 | # clearer and doesn't result in much overhead | |
135 | COMPREPLY=( $( mount | cut -d' ' -f 3 | grep ^$cur) ) | |
136 | return 0 | |
137 | } | |
138 | complete -F _umount umount | |
139 | ||
140 | # GID completion. This will get a list of all valid group names from | |
141 | # /etc/group and should work anywhere. | |
142 | # | |
143 | _gid_func () | |
144 | { | |
145 | local cur | |
146 | ||
147 | COMPREPLY=() | |
148 | cur=${COMP_WORDS[COMP_CWORD]} | |
149 | COMPREPLY=( $( awk 'BEGIN {FS=":"} {if ($1 ~ /^'$cur'/) print $1}' \ | |
150 | /etc/group ) ) | |
151 | return 0 | |
152 | } | |
153 | complete -F _gid_func groupdel groupmod | |
154 | ||
155 | # mount(8) completion. This will pull a list of possible mounts out of | |
156 | # /etc/fstab, unless the word being completed contains a ':', which | |
157 | # would indicate the specification of an NFS server. In that case, we | |
158 | # query the server for a list of all available exports and complete on | |
159 | # that instead. | |
160 | # | |
161 | _mount () | |
162 | ||
163 | { local cur | |
164 | ||
165 | COMPREPLY=() | |
166 | cur=${COMP_WORDS[COMP_CWORD]} | |
167 | ||
168 | case "$cur" in | |
169 | *:*) | |
170 | COMPREPLY=( $( /usr/sbin/showmount -e --no-headers ${cur%%:*} |\ | |
171 | grep ^${cur#*:} | awk '{print $1}')) | |
172 | return 0 | |
173 | ;; | |
174 | *) | |
175 | COMPREPLY=( $( awk '{if ($2 ~ /\//) print $2}' /etc/fstab | \ | |
176 | grep ^$cur )) | |
177 | return 0 | |
178 | ;; | |
179 | esac | |
180 | } | |
181 | complete -F _mount mount | |
182 | ||
183 | # Linux rmmod(1) completion. This completes on a list of all currently | |
184 | # installed kernel modules. | |
185 | # | |
186 | _rmmod () | |
187 | { | |
188 | local cur | |
189 | ||
190 | COMPREPLY=() | |
191 | cur=${COMP_WORDS[COMP_CWORD]} | |
192 | ||
193 | COMPREPLY=($( lsmod | awk '{if (NR != 1 && $1 ~ /^'$cur'/) print $1}')) | |
194 | return 0 | |
195 | } | |
196 | complete -F _rmmod rmmod | |
197 | ||
198 | # Linux insmod(1) completion. This completes on a list of all | |
199 | # available modules for the version of the kernel currently running. | |
200 | # | |
201 | _insmod () | |
202 | { | |
203 | local cur modpath | |
204 | ||
205 | COMPREPLY=() | |
206 | cur=${COMP_WORDS[COMP_CWORD]} | |
207 | modpath=/lib/modules/`uname -r` | |
208 | ||
209 | COMPREPLY=($( ls -R $modpath | sed -ne 's/^\('$cur'.*\)\.o$/\1/p')) | |
210 | return 0 | |
211 | } | |
212 | complete -F _insmod insmod depmod modprobe | |
213 | ||
214 | # man(1) completion. This relies on the security enhanced version of | |
215 | # GNU locate(1). UNIX variants having non-numeric man page sections | |
216 | # other than l, m and n should add the appropriate sections to the | |
217 | # first clause of the case statement. | |
218 | # | |
219 | # This is Linux specific, in that 'man <section> <page>' is the | |
220 | # expected syntax. This allows one to do something like | |
221 | # 'man 3 str<tab>' to obtain a list of all string handling syscalls on | |
222 | # the system. | |
223 | # | |
224 | _man () | |
225 | { | |
226 | local cur prev | |
227 | ||
228 | COMPREPLY=() | |
229 | cur=${COMP_WORDS[COMP_CWORD]} | |
230 | prev=${COMP_WORDS[COMP_CWORD-1]} | |
231 | ||
232 | case "$prev" in | |
233 | [0-9lmn]) | |
234 | COMPREPLY=($( slocate -ql 0 -r '/man/man'$prev'/'$cur | \ | |
235 | sed -ne 's/^.*\/\('$cur'[^.\/]*\)\..*$/\1/p' )) | |
236 | return 0 | |
237 | ;; | |
238 | *) | |
239 | COMPREPLY=($( slocate -ql 0 -r '/man/man./'$cur | \ | |
240 | sed -ne 's/^.*\/\('$cur'[^.\/]*\)\..*$/\1/p' )) | |
241 | return 0 | |
242 | ;; | |
243 | esac | |
244 | } | |
245 | complete -F _man man | |
246 | ||
247 | # Linux killall(1) completion. This wouldn't be much use on, say, | |
248 | # Solaris, where killall does exactly that: kills ALL processes. | |
249 | # | |
250 | # This could be improved. For example, it currently doesn't take | |
251 | # command line options into account | |
252 | # | |
253 | _killall () | |
254 | { | |
255 | local cur prev | |
256 | ||
257 | COMPREPLY=() | |
258 | cur=${COMP_WORDS[COMP_CWORD]} | |
259 | prev=${COMP_WORDS[COMP_CWORD-1]} | |
260 | ||
261 | case "$prev" in | |
262 | -[A-Z0-9]*) | |
263 | # get a list of processes (the first sed evaluation | |
264 | # takes care of swapped out processes, the second | |
265 | # takes care of getting the basename of the process) | |
266 | COMPREPLY=( $( ps ahx | awk '{if ($5 ~ /^'$cur'/) print $5}' | \ | |
267 | sed -e 's#[]\[]##g' -e 's#^.*/##' )) | |
268 | return 0 | |
269 | ;; | |
270 | esac | |
271 | ||
272 | # first parameter can be either a signal or a process | |
273 | if [ $COMP_CWORD -eq 1 ]; then | |
274 | # standard signal completion is rather braindead, so we need | |
275 | # to hack around to get what we want here, which is to | |
276 | # complete on a dash, followed by the signal name minus | |
277 | # the SIG prefix | |
278 | COMPREPLY=( $( compgen -A signal SIG${cur#-} )) | |
279 | for (( i=0; i < ${#COMPREPLY[@]}; i++ )); do | |
280 | COMPREPLY[i]=-${COMPREPLY[i]#SIG} | |
281 | done | |
282 | fi | |
283 | ||
284 | # get processes, adding to signals if applicable | |
285 | COMPREPLY=( ${COMPREPLY[*]} $( ps ahx | \ | |
286 | awk '{if ($5 ~ /^'$cur'/) print $5}' | \ | |
287 | sed -e 's#[]\[]##g' -e 's#^.*/##' )) | |
288 | return 0 | |
289 | } | |
290 | complete -F _killall killall | |
291 | ||
292 | # GNU find(1) completion. This makes heavy use of ksh style extended | |
293 | # globs and contains Linux specific code for completing the parameter | |
294 | # to the -fstype option. | |
295 | # | |
296 | _find () | |
297 | { | |
298 | local cur prev | |
299 | ||
300 | COMPREPLY=() | |
301 | cur=${COMP_WORDS[COMP_CWORD]#-} | |
302 | prev=${COMP_WORDS[COMP_CWORD-1]} | |
303 | ||
304 | case "$prev" in | |
305 | -@(max|min)depth) | |
306 | COMPREPLY=( $( compgen -W '0 1 2 3 4 5 6 7 8 9' ) ) | |
307 | return 0 | |
308 | ;; | |
309 | -?(a)newer|-fls|-fprint?(0|f)) | |
310 | COMPREPLY=( $( compgen -f $cur ) ) | |
311 | return 0 | |
312 | ;; | |
313 | -fstype) | |
314 | # this is highly non-portable (the option to -d is a tab) | |
315 | COMPREPLY=( $( cut -d' ' -f 2 /proc/filesystems | grep ^$cur ) ) | |
316 | return 0 | |
317 | ;; | |
318 | -gid) | |
319 | COMPREPLY=( $( awk 'BEGIN {FS=":"} \ | |
320 | {if ($3 ~ /^'$cur'/) print $3}' /etc/group ) ) | |
321 | return 0 | |
322 | ;; | |
323 | -group) | |
324 | COMPREPLY=( $( awk 'BEGIN {FS=":"} \ | |
325 | {if ($1 ~ /^'$cur'/) print $1}' /etc/group ) ) | |
326 | return 0 | |
327 | ;; | |
328 | -?(x)type) | |
329 | COMPREPLY=( $( compgen -W 'b c d p f l s' $cur ) ) | |
330 | return 0 | |
331 | ;; | |
332 | -uid) | |
333 | COMPREPLY=( $( awk 'BEGIN {FS=":"} \ | |
334 | {if ($3 ~ /^'$cur'/) print $3}' /etc/passwd ) ) | |
335 | return 0 | |
336 | ;; | |
337 | -user) | |
338 | COMPREPLY=( $( compgen -u $cur ) ) | |
339 | return 0 | |
340 | ;; | |
341 | -[acm]min|-[acm]time|-?(i)?(l)name|-inum|-?(i)path|-?(i)regex| \ | |
342 | -links|-perm|-size|-used|-exec|-ok|-printf) | |
343 | # do nothing, just wait for a parameter to be given | |
344 | return 0 | |
345 | ;; | |
346 | esac | |
347 | ||
348 | # complete using basic options ($cur has had its dash removed here, | |
349 | # as otherwise compgen will bomb out with an error, since it thinks | |
350 | # the dash is an option to itself) | |
351 | COMPREPLY=( $( compgen -W 'daystart depth follow help maxdepth \ | |
352 | mindepth mount noleaf version xdev amin anewer atime \ | |
353 | cmin cnewer ctime empty false fstype gid group ilname \ | |
354 | iname inum ipath iregex links lname mmin mtime name \ | |
355 | newer nouser nogroup perm regex size true type uid \ | |
356 | used user xtype exec fls fprint fprint0 fprintf ok \ | |
357 | print print0 printf prune ls' $cur ) ) | |
358 | ||
359 | # this removes any options from the list of completions that have | |
360 | # already been specified somewhere on the command line. | |
361 | COMPREPLY=( $( echo "${COMP_WORDS[@]}-" | \ | |
362 | (while read -d '-' i; do | |
363 | [ "$i" == "" ] && continue | |
364 | # flatten array with spaces on either side, | |
365 | # otherwise we cannot grep on word boundaries of | |
366 | # first and last word | |
367 | COMPREPLY=" ${COMPREPLY[@]} " | |
368 | # remove word from list of completions | |
369 | COMPREPLY=( ${COMPREPLY/ ${i%% *} / } ) | |
370 | done | |
371 | echo ${COMPREPLY[@]}) | |
372 | ) ) | |
373 | ||
374 | # put dashes back | |
375 | for (( i=0; i < ${#COMPREPLY[@]}; i++ )); do | |
376 | COMPREPLY[i]=-${COMPREPLY[i]} | |
377 | done | |
378 | ||
379 | return 0 | |
380 | } | |
381 | complete -F _find find | |
382 | ||
383 | # Linux ifconfig(8) completion | |
384 | # | |
385 | _ifconfig () | |
386 | { | |
387 | local cur | |
388 | ||
389 | COMPREPLY=() | |
390 | cur=${COMP_WORDS[COMP_CWORD]} | |
391 | ||
392 | case "${COMP_WORDS[1]}" in | |
393 | -|*[0-9]*) | |
394 | COMPREPLY=( $( compgen -W '-a up down arp promisc allmulti \ | |
395 | metric mtu dstaddr netmask add del \ | |
396 | tunnel irq io_addr mem_start media \ | |
397 | broadcast pointopoint hw multicast \ | |
398 | address txqueuelen' $cur )) | |
399 | COMPREPLY=( $( echo " ${COMP_WORDS[@]}" | \ | |
400 | (while read -d ' ' i; do | |
401 | [ "$i" == "" ] && continue | |
402 | # flatten array with spaces on either side, | |
403 | # otherwise we cannot grep on word | |
404 | # boundaries of first and last word | |
405 | COMPREPLY=" ${COMPREPLY[@]} " | |
406 | # remove word from list of completions | |
407 | COMPREPLY=( ${COMPREPLY/ $i / } ) | |
408 | done | |
409 | echo ${COMPREPLY[@]}) | |
410 | ) ) | |
411 | return 0 | |
412 | ;; | |
413 | esac | |
414 | ||
415 | COMPREPLY=( $( ifconfig -a | sed -ne 's/^\('$cur'[^ ]*\).*$/\1/p' )) | |
416 | } | |
417 | complete -F _ifconfig ifconfig | |
418 | ||
419 | # Linux ipsec(8) completion (for FreeS/WAN). Very basic. | |
420 | # | |
421 | _ipsec () | |
422 | { | |
423 | local cur | |
424 | ||
425 | COMPREPLY=() | |
426 | cur=${COMP_WORDS[COMP_CWORD]} | |
427 | ||
428 | COMPREPLY=( $( compgen -W 'auto barf eroute klipsdebug look manual \ | |
429 | pluto ranbits rsasigkey setup showdefaults \ | |
430 | showhostkey spi spigrp tncfg whack' $cur )) | |
431 | } | |
432 | complete -F _ipsec ipsec | |
433 | ######################################################################### |