Using some shell magic, we can write a function to do this sort of wrapping for us.

subalias () { local cmd body name name = "${1%%=*}" cmd = "${name%_*}" body = "${1#*=}" eval "$cmd"'() { local cmd='"$cmd"' subcmd="$1" if type "${cmd}_${subcmd}" >/dev/null 2>&1; then shift "${cmd}_${subcmd}" "$@" elif type "${cmd}_${subcmd}_subalias" >/dev/null 2>&1; then shift "${cmd}_${subcmd}_subalias" "$@" elif type "${cmd}_subalias" >/dev/null 2>&1; then "${cmd}_subalias" "$@" else command "$cmd" "$@" fi }' if test "$body" != "$1" ; then eval "${name}_subalias"'() { '"$body"'; }' fi }

Now you can just write subalias apt_log='less /var/log/apt/history.log' . Two functions will be generated for you: apt_log_subalias to open the log file, and the apt wrapper function to dynamically dispatch based on the arguments passed to it.

What else can we do with this? Try implicitly invoking sudo only when it's required:

for action in install reinstall remove purge autoremove update upgrade \ full-upgrade edit-sources; do subalias apt_${ action }= 'sudo apt '"${action}"' "$@"' done

Now running apt install hello will automatically invoke sudo , but apt show hello will not. There are a couple interesting things to note. One is that, unlike normal aliases, we had to explicitly append arguments to the command by ending it with "$@" . Two is that we were able to run subalias multiple times to define multiple subaliases for apt . (The wrapper functions didn't clobber each other and erase the previous subalias.)

OK, what about sub-subaliases? We can do that too!

subalias apt_show = 'command apt show "$@"' subalias apt_show_vim = 'apt show emacs'