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 | #########################################################################
|
---|