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 14shopt -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 20complete -d cd mkdir rmdir pushd 21 22# Make file commands see only files 23complete -f cat less more chown ln strip 24complete -f -X '*.gz' gzip 25complete -f -X '*.Z' compress 26complete -f -X '!*.+(Z|gz|tgz|Gz)' gunzip zcat zmore 27complete -f -X '!*.Z' uncompress zmore zcat 28complete -f -X '!*.+(gif|jpg|jpeg|GIF|JPG|bmp)' ee xv 29complete -f -X '!*.+(ps|PS|ps.gz)' gv 30complete -f -X '!*.+(dvi|DVI)' dvips xdvi dviselect dvitype 31complete -f -X '!*.+(pdf|PDF)' acroread xpdf 32complete -f -X '!*.texi*' makeinfo texi2dvi texi2html 33complete -f -X '!*.+(tex|TEX)' tex latex slitex 34complete -f -X '!*.+(mp3|MP3)' mpg123 35 36# kill sees only signals 37complete -A signal kill -P '%' 38 39# user commands see only users 40complete -u finger su usermod userdel passwd 41 42# bg completes with stopped jobs 43complete -A stopped -P '%' bg 44 45# other job commands 46complete -j -P '%' fg jobs disown 47 48# network commands complete with hostname 49complete -A hostname ssh rsh telnet rlogin ftp ping fping host traceroute \ 50 nslookup 51 52# export and others complete with shell variables 53complete -v export local readonly unset 54 55# set completes with set options 56complete -A setopt set 57 58# shopt completes with shopt options 59complete -A shopt shopt 60 61# helptopics 62complete -A helptopic help 63 64# unalias completes with aliases 65complete -a unalias 66 67# various commands complete with commands 68complete -c command type nohup exec nice eval strace gdb 69 70# bind completes with readline bindings (make this more intelligent) 71complete -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} 121complete -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} 138complete -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} 153complete -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} 181complete -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} 196complete -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} 212complete -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} 245complete -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} 290complete -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} 381complete -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} 417complete -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} 432complete -F _ipsec ipsec 433######################################################################### 434