433 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			Text
		
	
	
	
	
	
			
		
		
	
	
			433 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			Text
		
	
	
	
	
	
| #####
 | |
| #To: chet@po.cwru.edu, sarahmckenna@lucent.com
 | |
| #Message-Id: <slrn8mqioc.msb.ian@lovelorn.linuxcare.com>
 | |
| #Posted-To: comp.unix.shell, gnu.bash.bug
 | |
| #Subject: bash 2.04 programmable completion examples
 | |
| #Reply-To: ian@linuxcare.com, ian@caliban.org
 | |
| #Summary: examples of programmable completion for bash 2.04
 | |
| #Date: Thu, 13 Jul 2000 00:52:33 -0400 (EDT)
 | |
| #From: ianmacd@linuxcare.com (Ian Macdonald)
 | |
| #####
 | |
| 
 | |
| #########################################################################
 | |
| # Turn on extended globbing
 | |
| shopt -s extglob
 | |
| 
 | |
| # 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 chown ln strip
 | |
| 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:0:1}" = "-" ]; then return 0; fi
 | |
| 
 | |
| 	# first parameter on line or first since an option?
 | |
| 	if [ $COMP_CWORD -eq 1 ] || [ "${prev:0:1}" = "-" ]; then
 | |
| 		case "$cur" in
 | |
| 		[a-zA-Z]*.*)
 | |
| 			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
 | |
| 			return 0
 | |
| 			;;
 | |
| 		*)
 | |
| 			COMPREPLY=( $( compgen -u $cur -S '.' ) )
 | |
| 			return 0
 | |
| 			;;
 | |
| 		esac
 | |
| 	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]}
 | |
| 
 | |
| 	case "$cur" in
 | |
| 	*:*)
 | |
| 	      COMPREPLY=( $( /usr/sbin/showmount -e --no-headers ${cur%%:*} |\
 | |
| 			       grep ^${cur#*:} | awk '{print $1}'))
 | |
| 		return 0
 | |
| 		;;
 | |
| 	*)
 | |
| 		COMPREPLY=( $( awk '{if ($2 ~ /\//) print $2}' /etc/fstab | \
 | |
| 			       grep ^$cur ))
 | |
| 		return 0
 | |
| 		;;
 | |
| 	esac
 | |
| }
 | |
| 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=($( 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
 | |
| 
 | |
| # 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 <section> <page>' is the
 | |
| # expected syntax. This allows one to do something like
 | |
| # 'man 3 str<tab>' to obtain a list of all string handling syscalls on
 | |
| # the system.
 | |
| #
 | |
| _man ()
 | |
| {
 | |
| 	local cur prev
 | |
| 
 | |
|         COMPREPLY=()
 | |
|         cur=${COMP_WORDS[COMP_CWORD]}
 | |
|         prev=${COMP_WORDS[COMP_CWORD-1]}
 | |
| 
 | |
| 	case "$prev" in
 | |
| 	[0-9lmn])
 | |
| 		COMPREPLY=($( slocate -ql 0 -r '/man/man'$prev'/'$cur | \
 | |
| 		      sed -ne 's/^.*\/\('$cur'[^.\/]*\)\..*$/\1/p' ))
 | |
| 		return 0
 | |
| 		;;
 | |
| 	*)
 | |
| 		COMPREPLY=($( slocate -ql 0 -r '/man/man./'$cur | \
 | |
| 		      sed -ne 's/^.*\/\('$cur'[^.\/]*\)\..*$/\1/p' ))
 | |
| 		return 0
 | |
| 		;;
 | |
| 	esac
 | |
| }
 | |
| 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]}
 | |
| 
 | |
| 	case "$prev" in
 | |
| 	-[A-Z0-9]*)
 | |
| 		# get a list of processes (the first sed evaluation
 | |
| 		# takes care of swapped out processes, the second
 | |
| 		# takes care of getting the basename of the process)
 | |
| 	       COMPREPLY=( $( ps ahx | awk '{if ($5 ~ /^'$cur'/) print $5}' | \
 | |
| 			       sed -e 's#[]\[]##g' -e 's#^.*/##' ))
 | |
| 		return 0
 | |
| 		;;
 | |
| 	esac
 | |
| 
 | |
| 	# 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 ($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 ))
 | |
| }
 | |
| complete -F _ipsec ipsec
 | |
| #########################################################################
 | 
