# bash_completion - some programmable completion functions for bash 2.04 # # # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software Foundation, # Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # Turn on extended globbing and programmable completion shopt -s extglob progcomp # A lot of the following one-liners were taken directly from the # completion examples provided with the bash 2.04 source distribution # Make directory commands see only directories complete -d cd mkdir rmdir pushd # Make file commands see only files complete -f cat less more ln strip complete -f -X '*.bz2' bzip2 complete -f -X '!*.bz2' bunzip2 complete -f -X '!*.zip' unzip complete -f -X '*.gz' gzip complete -f -X '*.Z' compress complete -f -X '!*.+(Z|gz|tgz|Gz)' gunzip zcat zmore complete -f -X '!*.Z' uncompress zmore zcat complete -f -X '!*.+(gif|jpg|jpeg|GIF|JPG|bmp)' ee xv complete -f -X '!*.+(ps|PS|ps.gz)' gv complete -f -X '!*.+(dvi|DVI)' dvips xdvi dviselect dvitype complete -f -X '!*.+(pdf|PDF)' acroread xpdf complete -f -X '!*.texi*' makeinfo texi2dvi texi2html complete -f -X '!*.+(tex|TEX)' tex latex slitex complete -f -X '!*.+(mp3|MP3)' mpg123 # kill sees only signals complete -A signal kill -P '%' # user commands see only users complete -u finger su usermod userdel passwd # bg completes with stopped jobs complete -A stopped -P '%' bg # other job commands complete -j -P '%' fg jobs disown # network commands complete with hostname complete -A hostname ssh rsh telnet rlogin ftp ping fping host traceroute \ nslookup # export and others complete with shell variables complete -v export local readonly unset # set completes with set options complete -A setopt set # shopt completes with shopt options complete -A shopt shopt # helptopics complete -A helptopic help # unalias completes with aliases complete -a unalias # various commands complete with commands complete -c command type nohup exec nice eval strace gdb # bind completes with readline bindings (make this more intelligent) complete -A binding bind # Now we get to the meat of the file, the functions themselves. Some # of these are works in progress. Most assume GNU versions of the # tools in question and may require modifications for use on vanilla # UNIX systems. # # A couple of functions may have non-portable, Linux specific code in # them, but this will be noted where applicable # GNU chown(1) completion. This should be expanded to allow the use of # ':' as well as '.' as the user.group separator. # _chown() { local cur prev user group COMPREPLY=() cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} # do not attempt completion if we're specifying an option if [ "$cur" == -* ]; then return 0; fi # first parameter on line or first since an option? if [ $COMP_CWORD -eq 1 ] || [[ "$prev" == -* ]]; then if [[ "$cur" == [a-zA-Z]*.* ]]; then user=${cur%.*} group=${cur#*.} COMPREPLY=( $( awk 'BEGIN {FS=":"} \ {if ($1 ~ /^'$group'/) print $1}' \ /etc/group ) ) for (( i=0; i < ${#COMPREPLY[@]}; i++ )); do COMPREPLY[i]=$user.${COMPREPLY[i]} done else COMPREPLY=( $( compgen -u $cur -S '.' ) ) fi else COMPREPLY=( $( compgen -f $cur ) ) fi return 0 } complete -F _chown chown # umount(8) completion. This relies on the mount point being the third # space-delimited field in the output of mount(8) # _umount() { local cur COMPREPLY=() cur=${COMP_WORDS[COMP_CWORD]} # could rewrite the cut | grep to be a sed command, but this is # clearer and doesn't result in much overhead COMPREPLY=( $( mount | cut -d' ' -f 3 | grep ^$cur) ) return 0 } complete -F _umount umount # GID completion. This will get a list of all valid group names from # /etc/group and should work anywhere. # _gid_func() { local cur COMPREPLY=() cur=${COMP_WORDS[COMP_CWORD]} COMPREPLY=( $( awk 'BEGIN {FS=":"} {if ($1 ~ /^'$cur'/) print $1}' \ /etc/group ) ) return 0 } complete -F _gid_func groupdel groupmod # mount(8) completion. This will pull a list of possible mounts out of # /etc/fstab, unless the word being completed contains a ':', which # would indicate the specification of an NFS server. In that case, we # query the server for a list of all available exports and complete on # that instead. # _mount() { local cur COMPREPLY=() cur=${COMP_WORDS[COMP_CWORD]} if [[ "$cur" == *:* ]]; then COMPREPLY=( $( /usr/sbin/showmount -e --no-headers ${cur%%:*} |\ grep ^${cur#*:} | awk '{print $1}')) else COMPREPLY=( $( awk '{if ($2 ~ /\//) print $2}' /etc/fstab | \ grep ^$cur )) fi return 0 } complete -F _mount mount # Linux rmmod(1) completion. This completes on a list of all currently # installed kernel modules. # _rmmod() { local cur COMPREPLY=() cur=${COMP_WORDS[COMP_CWORD]} COMPREPLY=($( /sbin/lsmod | \ awk '{if (NR != 1 && $1 ~ /^'$cur'/) print $1}')) return 0 } complete -F _rmmod rmmod # Linux insmod(1) completion. This completes on a list of all # available modules for the version of the kernel currently running. # _insmod() { local cur modpath COMPREPLY=() cur=${COMP_WORDS[COMP_CWORD]} modpath=/lib/modules/`uname -r` COMPREPLY=($( ls -R $modpath | sed -ne 's/^\('$cur'.*\)\.o$/\1/p')) return 0 } complete -F _insmod insmod depmod modprobe modinfo # man(1) completion. This relies on the security enhanced version of # GNU locate(1). UNIX variants having non-numeric man page sections # other than l, m and n should add the appropriate sections to the # first clause of the case statement. # # This is Linux specific, in that 'man
' is the # expected syntax. This allows one to do something like # 'man 3 str' to obtain a list of all string handling syscalls on # the system. # _man() { local cur prev cmd COMPREPLY=() cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} # pathname completion if parameter starts with / if [ "$cur" = "/" ]; then COMPREPLY=( $( compgen -f $cur ) ) return 0 fi if [[ "$prev" == [0-9n] ]]; then # churn out a string of paths to search, with * appended to $cur cmd=`awk '{if ($1 ~ /^MANPATH/) \ print $(NF)"/man'$prev'/'$cur'*"}' /etc/man.config | \ sort -u` # strip off * from paths ending in /* cmd=${cmd//\/\\*/\/} # redirect stderr for when path doesn't exist cmd="ls $cmd 2>/dev/null" COMPREPLY=( $( eval $cmd ) ) # get basename of man pages COMPREPLY=( ${COMPREPLY[@]##*/} ) # strip suffix from man pages COMPREPLY=( ${COMPREPLY[@]%.gz} ) COMPREPLY=( ${COMPREPLY[@]%.*} ) else cmd=`awk '{if ($1 ~ /^MANPATH/) \ print $(NF)"/man?/'$cur'*"}' /etc/man.config | sort -u` cmd=${cmd//\/\\*/\/} cmd="ls $cmd 2>/dev/null" COMPREPLY=( $( eval $cmd ) ) COMPREPLY=( ${COMPREPLY[@]##*/} ) COMPREPLY=( ${COMPREPLY[@]%.gz} ) COMPREPLY=( ${COMPREPLY[@]%.*} ) fi return 0 } complete -F _man man # Linux killall(1) completion. This wouldn't be much use on, say, # Solaris, where killall does exactly that: kills ALL processes. # # This could be improved. For example, it currently doesn't take # command line options into account # _killall() { local cur prev COMPREPLY=() cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} if [[ "$prev" == -[A-Z0-9]* ]]; then # get a list of processes (the gensub() in the awk takes care # of getting the basename of the process, the first sed # evaluation takes care of swapped out processes, and the # second takes care of getting the basename of the process) COMPREPLY=( $( ps ahx | \ awk '{if (gensub("^.*/","",1,$5) ~ /^'$cur'/) print $5}' | \ sed -e 's#[]\[]##g' -e 's#^.*/##' )) return 0 fi # first parameter can be either a signal or a process if [ $COMP_CWORD -eq 1 ]; then # standard signal completion is rather braindead, so we need # to hack around to get what we want here, which is to # complete on a dash, followed by the signal name minus # the SIG prefix COMPREPLY=( $( compgen -A signal SIG${cur#-} )) for (( i=0; i < ${#COMPREPLY[@]}; i++ )); do COMPREPLY[i]=-${COMPREPLY[i]#SIG} done fi # get processes, adding to signals if applicable COMPREPLY=( ${COMPREPLY[*]} $( ps ahx | \ awk '{if (gensub("^.*/","",1,$5) ~ /^'$cur'/) print $5}' | \ sed -e 's#[]\[]##g' -e 's#^.*/##' )) return 0 } complete -F _killall killall # GNU find(1) completion. This makes heavy use of ksh style extended # globs and contains Linux specific code for completing the parameter # to the -fstype option. # _find() { local cur prev COMPREPLY=() cur=${COMP_WORDS[COMP_CWORD]#-} prev=${COMP_WORDS[COMP_CWORD-1]} case "$prev" in -@(max|min)depth) COMPREPLY=( $( compgen -W '0 1 2 3 4 5 6 7 8 9' ) ) return 0 ;; -?(a)newer|-fls|-fprint?(0|f)) COMPREPLY=( $( compgen -f $cur ) ) return 0 ;; -fstype) # this is highly non-portable (the option to -d is a tab) COMPREPLY=( $( cut -d' ' -f 2 /proc/filesystems | grep ^$cur ) ) return 0 ;; -gid) COMPREPLY=( $( awk 'BEGIN {FS=":"} \ {if ($3 ~ /^'$cur'/) print $3}' /etc/group ) ) return 0 ;; -group) COMPREPLY=( $( awk 'BEGIN {FS=":"} \ {if ($1 ~ /^'$cur'/) print $1}' /etc/group ) ) return 0 ;; -?(x)type) COMPREPLY=( $( compgen -W 'b c d p f l s' $cur ) ) return 0 ;; -uid) COMPREPLY=( $( awk 'BEGIN {FS=":"} \ {if ($3 ~ /^'$cur'/) print $3}' /etc/passwd ) ) return 0 ;; -user) COMPREPLY=( $( compgen -u $cur ) ) return 0 ;; -[acm]min|-[acm]time|-?(i)?(l)name|-inum|-?(i)path|-?(i)regex| \ -links|-perm|-size|-used|-exec|-ok|-printf) # do nothing, just wait for a parameter to be given return 0 ;; esac # complete using basic options ($cur has had its dash removed here, # as otherwise compgen will bomb out with an error, since it thinks # the dash is an option to itself) COMPREPLY=( $( compgen -W 'daystart depth follow help maxdepth \ mindepth mount noleaf version xdev amin anewer atime \ cmin cnewer ctime empty false fstype gid group ilname \ iname inum ipath iregex links lname mmin mtime name \ newer nouser nogroup perm regex size true type uid \ used user xtype exec fls fprint fprint0 fprintf ok \ print print0 printf prune ls' $cur ) ) # this removes any options from the list of completions that have # already been specified somewhere on the command line. COMPREPLY=( $( echo "${COMP_WORDS[@]}-" | \ (while read -d '-' i; do [ "$i" == "" ] && continue # flatten array with spaces on either side, # otherwise we cannot grep on word boundaries of # first and last word COMPREPLY=" ${COMPREPLY[@]} " # remove word from list of completions COMPREPLY=( ${COMPREPLY/ ${i%% *} / } ) done echo ${COMPREPLY[@]}) ) ) # put dashes back for (( i=0; i < ${#COMPREPLY[@]}; i++ )); do COMPREPLY[i]=-${COMPREPLY[i]} done return 0 } complete -F _find find # Linux ifconfig(8) completion # _ifconfig() { local cur COMPREPLY=() cur=${COMP_WORDS[COMP_CWORD]} case "${COMP_WORDS[1]}" in -|*[0-9]*) COMPREPLY=( $( compgen -W '-a up down arp promisc allmulti \ metric mtu dstaddr netmask add del \ tunnel irq io_addr mem_start media \ broadcast pointopoint hw multicast \ address txqueuelen' $cur )) COMPREPLY=( $( echo " ${COMP_WORDS[@]}" | \ (while read -d ' ' i; do [ "$i" == "" ] && continue # flatten array with spaces on either side, # otherwise we cannot grep on word # boundaries of first and last word COMPREPLY=" ${COMPREPLY[@]} " # remove word from list of completions COMPREPLY=( ${COMPREPLY/ $i / } ) done echo ${COMPREPLY[@]}) ) ) return 0 ;; esac COMPREPLY=( $( ifconfig -a | sed -ne 's/^\('$cur'[^ ]*\).*$/\1/p' )) } complete -F _ifconfig ifconfig # Linux ipsec(8) completion (for FreeS/WAN). Very basic. # _ipsec() { local cur COMPREPLY=() cur=${COMP_WORDS[COMP_CWORD]} COMPREPLY=( $( compgen -W 'auto barf eroute klipsdebug look manual \ pluto ranbits rsasigkey setup showdefaults \ showhostkey spi spigrp tncfg whack' $cur )) return 0 } complete -F _ipsec ipsec # cvs(1) completion # _cvs() { local cur prev COMPREPLY=() cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} if [ $COMP_CWORD -eq 1 ] || [[ "$prev" == -* ]]; then COMPREPLY=( $( compgen -W 'add admin checkout commit diff \ export history import log rdiff release remove rtag status \ tag update' $cur )) else COMPREPLY=( $( compgen -f $cur )) fi return 0 } complete -F _cvs cvs # rpm(8) completion. This isn't exhaustive yet, but still provides # quite a lot of functionality. # _rpm() { dashify() { local i for (( i=0; i < ${#COMPREPLY[@]}; i++ )); do if [ ${#COMPREPLY[i]} -le 2 ]; then COMPREPLY[i]=-${COMPREPLY[i]} else COMPREPLY[i]=--${COMPREPLY[i]} fi done } local cur cur_nodash prev COMPREPLY=() cur=${COMP_WORDS[COMP_CWORD]} cur_nodash=${cur#-} prev=${COMP_WORDS[COMP_CWORD-1]} if [ $COMP_CWORD = 1 ]; then # first parameter on line case "$cur" in -b*) COMPREPLY=( $( compgen -W 'ba bb bc bi bl bp bs' \ $cur_nodash ) ) dashify return 0 ;; -t*) COMPREPLY=( $( compgen -W 'ta tb tc ti tl tp ts' \ $cur_nodash ) ) dashify return 0 ;; --*) COMPREPLY=( $( compgen -W 'help version initdb \ checksig recompile rebuild resign addsign rebuilddb \ showrc setperms setgids' ${cur_nodash#-} ) ) dashify return 0 ;; *) COMPREPLY=( $( compgen -W 'b e F i q t U V' \ $cur_nodash ) ) dashify return 0 ;; esac fi case "$prev" in --@(db|exclude)path|prefix|relocate|root) COMPREPLY=( $( compgen -d $cur ) ) return 0 ;; esac case "${COMP_WORDS[1]}" in -[iFU]*) # complete on list of relevant options COMPREPLY=( $( compgen -W 'percent force test replacepkgs \ replacefiles root excludedocs includedocs noscripts rcfile \ ignorearch dbpath prefix ignoreos nodeps allfiles ftpproxy \ ftpport justdb httpproxy httpport noorder relocate badreloc \ notriggers excludepath ignoresize oldpackage' ${cur_nodash#-} )) dashify # return if $cur is an option [[ "$cur" == -* ]] && return 0 # add a list of RPMS to possible completions COMPREPLY=( ${COMPREPLY[@]} $( compgen -G $cur\*.rpm ) ) return 0 ;; -qp*) # complete on list of relevant options COMPREPLY=( $( compgen -W 'scripts root rcfile whatprovides \ whatrequires requires triggeredby ftpport ftpproxy httpproxy \ httpport provides triggers dump changelog dbpath filesbypkg' \ ${cur_nodash#-} ) ) dashify # return if $cur is an option [[ "$cur" == -* ]] && return 0 # add a list of RPMS to possible completions COMPREPLY=( ${COMPREPLY[@]} $( compgen -G $cur\*.rpm ) ) return 0 ;; -*f) # standard filename completion COMPREPLY=( $( compgen -f $cur ) ) return 0 ;; -e) # complete on list of relevant options COMPREPLY=( $( compgen -W 'allmatches noscripts notriggers \ nodeps test' ${cur_nodash#-} ) ) dashify # return if $cur is an option [[ "$cur" == -* ]] && return 0 # complete on basename of installed RPMs COMPREPLY=( $( rpm -qa | \ sed -ne 's/^\('$cur'.*\)-[0-9a-zA-Z._]\+-[0-9.]\+$/\1/p' ) ) return 0 ;; -qa*) # complete on list of relevant options COMPREPLY=( $( compgen -W 'scripts root rcfile whatprovides \ whatrequires requires triggeredby ftpport ftpproxy httpproxy \ httpport provides triggers dump changelog dbpath specfile \ querybynumber last filesbypkg' ${cur_nodash#-} ) ) dashify return 0 ;; -q*) # complete on list of relevant options COMPREPLY=( $( compgen -W 'scripts root rcfile whatprovides \ whatrequires requires triggeredby ftpport ftpproxy httpproxy \ httpport provides triggers dump changelog dbpath specfile \ querybynumber last filesbypkg' ${cur_nodash#-} ) ) dashify # return if $cur is an option [[ "$cur" == -* ]] && return 0 # add a list of RPMS to possible completions COMPREPLY=( ${COMPREPLY[@]} $( rpm -qa | \ sed -ne 's/^\('$cur'.*\)-[0-9a-zA-Z._]\+-[0-9.]\+$/\1/p' ) ) return 0 ;; -[Vy]*) # complete on list of relevant options COMPREPLY=( $( compgen -W 'root rcfile dbpath nodeps nofiles \ noscripts nomd5 nopgp' ${cur_nodash#-} ) ) dashify # return if $cur is an option [[ "$cur" == -* ]] && return 0 # add a list of RPMS to possible completions COMPREPLY=( ${COMPREPLY[@]} $( rpm -qa | \ sed -ne 's/^\('$cur'.*\)-[0-9a-zA-Z._]\+-[0-9.]\+$/\1/p' ) ) return 0 ;; -b*) # complete on list of relevant options COMPREPLY=( $( compgen -W 'short-circuit timecheck clean \ rmsource test sign buildroot target buildarch buildos' \ ${cur_nodash#-} ) ) dashify # return if $cur is an option [[ "$cur" == -* ]] && return 0 # complete on .spec files COMPREPLY=( $( compgen -G $cur\*.spec ) ) return 0 ;; -t*) # complete on list of relevant options COMPREPLY=( $( compgen -W 'short-circuit timecheck clean \ rmsource test sign buildroot target buildarch buildos' \ ${cur_nodash#-} ) ) dashify # return if $cur is an option [[ "$cur" == -* ]] && return 0 # complete on .tar.gz files COMPREPLY=( $( compgen -G $cur\*.tar.gz ) ) return 0 ;; --re@(build|compile)) # complete on source RPMs COMPREPLY=( $( compgen -G $cur\*.src.rpm ) ) return 0 ;; --@(checksig|@(re|add)sign)) # complete on RPMs COMPREPLY=( $( compgen -G $cur\*.rpm ) ) return 0 ;; --set@(perms|gids)) # complete on installed RPMs COMPREPLY=( ${COMPREPLY[@]} $( rpm -qa | \ sed -ne 's/^\('$cur'.*\)-[0-9a-zA-Z._]\+-[0-9.]\+$/\1/p' ) ) return 0 ;; esac return 0 } complete -F _rpm rpm # chsh(1) completion # _chsh() { local cur prev COMPREPLY=() cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} if [ "$prev" = "-s" ]; then COMPREPLY=( $( chsh -l | grep ^$cur ) ) else COMPREPLY=( $( compgen -u $cur ) ) fi return 0 } complete -F _chsh chsh # chkconfig(8) completion # _chkconfig() { local cur prev COMPREPLY=() cur=${COMP_WORDS[COMP_CWORD]} cur_nodash=${cur#--} prev=${COMP_WORDS[COMP_CWORD-1]} if [ $COMP_CWORD -eq 1 ]; then COMPREPLY=( $( compgen -W 'list add del level' $cur_nodash ) ) for (( i=0; i < ${#COMPREPLY[@]}; i++ )); do COMPREPLY[i]=--${COMPREPLY[i]} done return 0 fi if [ $COMP_CWORD -eq 4 ]; then COMPREPLY=( $( compgen -W 'on off reset' $cur ) ) return 0 fi case "$prev" in @([1-6]|--@(list|add|del))) COMPREPLY=( $( compgen -W "`(cd /etc/rc.d/init.d; echo *)`" \ $cur) ) return 0 ;; --level) COMPREPLY=( $( compgen -W '1 2 3 4 5 6' $cur ) ) return 0 ;; esac return 0 } complete -F _chkconfig chkconfig # ssh(1) completion. Should be able to improve this with user@host notation, # but the '@' seems to trigger some kind of bug in bash's completion. # _ssh() { local cur prev kh kh=() COMPREPLY=() cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} case "$prev" in -*c) COMPREPLY=( $( compgen -W 'blowfish 3des 3des-cbc blowfish-cbc \ arcfour cast128-cbc' $cur ) ) return 0 ;; -*l) COMPREPLY=( $( compgen -u $cur ) ) return 0 ;; esac # Host has been specified, so now do simple command completion if [ $COMP_CWORD -gt 1 ]; then COMPREPLY=( $( compgen -c $cur ) ) return 0 fi [ -r /etc/known_hosts ] && kh[0]=/etc/known_hosts [ -r ~/.ssh/known_hosts ] && kh[1]=~/.ssh/known_hosts # If we have known_hosts files to use if [ ${#kh[@]} -gt 0 ]; then if [[ $cur == [0-9]*.* ]]; then # Digits followed by a dot - just search for that cur="^$cur.*" elif [[ $cur == [0-9]* ]]; then # Digits followed by no dot - search for digits followed # by a dot cur="^$cur.*\." elif [ -z $cur ]; then # A blank - search for a dot or an alpha character cur="[a-z.]" else cur="^$cur" fi # FS needs to look for a comma separated list COMPREPLY=( $( awk 'BEGIN {FS="[ ,]"} {for (i=1; i<=NR; ++i) { \ if ($i ~ /'$cur'/) {print $i} \ }}' ${kh[@]} ) ) else # Just do normal hostname completion COMPREPLY=( $( compgen -A hostname $cur ) ) fi return 0 } complete -F _ssh ssh slogin # Linux route(8) completion. This could be improved by adding address family # completion for -A, etc. # _route() { local cur prev COMPREPLY=() cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} if [ "$prev" = dev ]; then COMPREPLY=( $( ifconfig -a | sed -ne 's/^\('$cur'[^ ]*\).*$/\1/p' )) return 0 fi # Must use grep here, otherwise $cur will cause compgen to barf, if # it begins with a hyphen COMPREPLY=( $( compgen -W 'add del -host -net netmask metric mss \ window irtt reject mod dyn reinstate dev' | \ grep ^$cur ) ) COMPREPLY=( $( echo " ${COMP_WORDS[@]}" | \ (while read -d ' ' i; do [ "$i" == "" ] && continue # flatten array with spaces on either side, # otherwise we cannot grep on word # boundaries of first and last word COMPREPLY=" ${COMPREPLY[@]} " # remove word from list of completions COMPREPLY=( ${COMPREPLY/ $i / } ) done echo ${COMPREPLY[@]}) ) ) return 0 } complete -F _route route # ]]>