[Bash-completion-devel] Alias handling
Mario Schwalbe
m3o.s6e at googlemail.com
Wed Jul 30 08:54:15 UTC 2014
Hi,
I'm using the great bash-completion package for quite a while now,
but I find it lacks a good support of aliases. (Yes, I know they are
deprecated in favour of shell functions, but there are many people
out there which use them a lot.)
Therefore, I'd like to propose the attached code to be included in a
future release. It basically rebinds the completion of the aliased
command once completion of an alias is attempted.
However, there are some caveats:
(a) An aliased command must not contain whitespace in its name, as
it is used to split words.
(b) Does not support semantically broken aliases, like:
alias foo='SomeCommand --user'
because the rebound completion handler of SomeCommand cannot see
that the previous word would be --user to propose user names.
(c) Currently, handles sudo and the _longopt completion function specially.
There might be other commands/functions where that could be necessary.
If you are interested/have further questions/suggestions, drop me an email.
ciao,
Mario
-------------- next part --------------
#
# vim: set tabstop=4 shiftwidth=4 noexpandtab filetype=sh:
#
# File:
# GenericAliases.bash
#
# Description:
# Bash-completion helpers to handle aliases.
#
# Author(s):
# (c) 2012-2014 Mario Schwalbe (m3o.s6e at googlemail.com)
#
#
# Get aliased command for alias ($1).
# Returns: 0 on success, 1 otherwise.
#
__get_aliased_command()
{
local aliasFor=$(alias "$1" 2> /dev/null)
[ -z "$aliasFor" ] && return 1
# strip alias specification stuff
aliasFor=${aliasFor#*\'}
aliasFor=${aliasFor%\'}
# remove leading and trailing spaces
aliasFor=${aliasFor##+( )}
aliasFor=${aliasFor%%+( )}
# strip first word of command unless like `alias so=sudo -E'
if [[ "$aliasFor" == "sudo "* ]] ; then
local w
for w in ${aliasFor#* } ; do
if [[ "$w" != -* ]] ; then
aliasFor=$w
break
fi
done
fi
# get first word of command
aliasFor=${aliasFor%% *}
# stip path to avoid endless recursion due to `alias ps=/bin/ps fax'
aliasFor=${aliasFor##*/}
echo $aliasFor
}
#
# Rebind previously existing completion of a command ($2) to another command ($1).
# Returns: 0 on success, 1 otherwise.
#
__rebind_completion()
{
if [ $# -lt 2 ] ; then
echo "__rebind_completion: Missing arument(s): $@" > /dev/stderr
return 1
fi
local cmd=$1 aliasFor=$2
# get completion
local oldComp=$(complete -p "$aliasFor" 2> /dev/null)
if [ -n "$oldComp" ] ; then
# replace trailing command part
local newComp=${oldComp/% $aliasFor/ $cmd}
if [ "$newComp" != "$oldComp" ] ; then
if [[ "$newComp" == *"-F _longopt "* ]] ; then
eval "
__${cmd}_longopt()
{
_longopt $aliasFor \${@:2}
}
"
newComp=${newComp/_longopt/__${cmd}_longopt}
fi
$newComp > /dev/null 2>&1
return $?
fi
fi
return 1
}
#
# Lookup and rebind previously existing completion of a command ($2) to another command ($1).
# Returns: 0 on success, 1 otherwise.
#
__lookup_completion()
{
if [ $# -lt 2 ] ; then
echo "__lookup_completion: Missing arument(s): $@" > /dev/stderr
return 1
fi
local cmd=$1 aliasFor=$2
if complete -p "$aliasFor" > /dev/null 2>&1 ; then
# have completion: rebind
__rebind_completion $cmd "$aliasFor"
return $?
elif [ "$aliasFor" != "${aliasFor##*/}" ] ; then
# aliased command contained a path: retry without
__lookup_completion $cmd "${aliasFor##*/}"
return $?
else
# did not find anything: try to expand one more time
local nextAliasFor=$(__get_aliased_command "$aliasFor")
# break recursion in case of `alias ls=ls --color=auto' or end of list
if [ -n "$nextAliasFor" -a "$nextAliasFor" != "$aliasFor" ] ; then
__lookup_completion $cmd "$nextAliasFor"
return $?
else
_completion_loader "$aliasFor"
__rebind_completion $cmd "$aliasFor"
return $?
fi
fi
echo "__lookup_completion: FIXME: unexpectedly reached end" > /dev/stderr
return 1
}
#
# Default completion handler to rebind aliases on demand.
# Parameters:
# 1. command/alias ... command/alias to resolve completion for
# 2. current word ... (see BASH completions)
# 3. previous word ... (see BASH completions)
# Returns:
# 0 on success, 124 retry completion, 1 otherwise.
#
__lookup_completion_handler()
{
local cmd=$1 aliasFor=$(__get_aliased_command "$1")
if [ -n "$aliasFor" ] ; then
# try to resolve chains of aliases and rebind completion
__lookup_completion $cmd "$aliasFor" && return 124
return $?
else
# not an alias: call default implementation
_completion_loader $cmd
return $?
fi
}
# overwrite default completion handler to also lookup aliases
complete -F __lookup_completion_handler -D
# ***** end of source *****
More information about the Bash-completion-devel
mailing list