Programmatically dereference/resolve aliases in ba

2019-06-03 07:52发布

问题:

I need to determine which command a shell alias resolves to in bash, programmatically; i.e., I need to write a bash function that will take a name potentially referring to an alias and return the "real" command it ultimately refers to, recursing through chains of aliases where applicable.

For example, given the following aliases:

alias dir='list -l'
alias list='ls'

where my function is dereference_alias,

dereference_alias list    # returns "ls"
dereference_alias dir     # also returns "ls"

Is there some builtin I don't know about that does this neatly, or shall I resign myself to scraping the output of alias?

回答1:

Here's a version I wrote that does not rely on any external commands and also handles recursive aliases without creating an infinite loop:

# Arguments:
#
#   $1  Command to compact using aliases
#
function command-to-alias()
{
    local alias_key
    local expansion
    local guess

    local command="$1"
    local search_again="x"
    local shortest_guess="$command"

    while [[ "${search_again:-}" ]]; do
        unset search_again
        for alias_key in "${!BASH_ALIASES[@]}"; do
            expansion="${BASH_ALIASES[$alias_key]}"
            guess="${command/#"$expansion"/$alias_key}"
            test "${#guess}" -lt "${#shortest_guess}" || continue
            shortest_guess="$guess"
            search_again="x"
        done
        command="$shortest_guess"
    done

    echo "$command"
}


回答2:

Here's how I'm doing it, though I'm not sure it's the best way:

dereference_alias () {
  # recursively expand alias, dropping arguments
  # output == input if no alias matches
  local p
  local a="$1"
  if [[ "alias" -eq $(type -t $a) ]] && p=$(alias "$a" 2>&-); then
    dereference_alias $(sed -re "s/alias "$a"='(\S+).*'$/\1/" <<< "$p")
  else
    echo $a
  fi
}

The major downsides here are that I rely on sed, and my means of dropping any arguments in the alias stops at the first space, expecting that no alias shall ever point to a program which, for some reason, has spaces in its name (i.e. alias badprogram='A\ Very\ Bad\ Program --some-argument'), which is a reasonable enough assumption, but still. I think that at least the whole sed part could be replaced by maybe something leveraging bash's own parsing/splitting/tokenization of command lines, but I wouldn't know where to begin.