1

Bash has the built-in command "type", which indicates how each argument would be interpreted if used as a command name, for instance:

$ type myfunction
myfunction is a function
myfunction () 
{ 
    echo hello
}

$ type myfunctionalias
myfunctionalias is aliased to `myfunction'

$ type python
python is /usr/bin/python

In the last case, /usr/bin/python is a link, and its target is again a link:

$ ls -l /usr/bin/python
lrwxrwxrwx 1 root root 7 Feb  5 09:05 /usr/bin/python -> python3

$ ls -l /usr/bin/python3
lrwxrwxrwx 1 root root 7 Feb  5 09:05 /usr/bin/python -> python3.13

$ ls -l /usr/bin/python3.13
-rwxr-xr-x 1 root root 14352 Feb  5 09:05 /usr/bin/python3.13

It is possible to resolve a link with "readlink -f", however it is tedious to do it manually, since i requires up to three steps: type to resolve the alias source, type to resolve the alias target, and readlink to resolve any link.

In practice, I am more often interested in what will actually be executed, rather than the single resolution steps. So I need a "type"-like tool that resolves the aliases and the links, ideally:

$ clevertype python
/usr/bin/python3.13

$ clevertype --verbose python
python is /usr/bin/python
/usr/bin/python links to /usr/bin/python3
/usr/bin/python3 links to /usr/bin/python3.13
Result: python resolves to /usr/bin/python3.13

And:

$ clevertype pythonalias
pythonalias resolves to /usr/bin/python3.13

$ clevertype --verbose pythonalias
pythonalias is aliased to `/usr/bin/python'
/usr/bin/python links to /usr/bin/python3
/usr/bin/python3 links to /usr/bin/python3.13
Result: pythonalias resolves to /usr/bin/python3.13

Does such a tool exist?

4
  • 1
    What if the symlink points to a file that is a shell script that calls another tool? How far do you want to go, and why? Commented Mar 13 at 1:49
  • My main use cases currently are alias/links to tools in Python virtual environments and links caused by "/etc/alternatives". So resolving simple "mappings" like aliases and links is good enough for me. Commented Mar 13 at 6:42
  • "interested in what will actually be executed, [...] I need a type-like tool that resolves the aliases and the links" => I don't think you can "resolve" an "alias" recursively as it can contain shell code. Commented Mar 14 at 9:03
  • I am only interested in simple aliases, but as I just found out, the function proposed below did not even work with alias ls="ls [options]", which is pretty common. I updated the code to handle also this case, but of course, it might not work for more complex alias cases. Commented Mar 14 at 15:16

3 Answers 3

1

Assuming that there is no solution out of the box, I expanded Jukka Matilainen answer to include the resolution of aliases, recursion, and print the steps.

clevertype ()
{
    input="$1"
    force_file=
    _clevertype() {
        if [[ -n $force_file || "$(type -t "$1")" == "file" ]]; then
            if [[ $(type -p "$1") != "$1" ]]; then
                echo "Resolving from PATH:  $1 -> $(type -P "$1")"
                _clevertype $(type -P "$1")
            elif [[ -L "$1" ]]; then
                echo "Resolving links:      $1 -> $(readlink -e "$(type -p "$1")")"
                _clevertype $(type -P "$(readlink -e "$(type -P "$1")")")
            else
                echo "$input is $1"
            fi
        elif [[ "$(type -t "$1")" == "alias" ]]; then
            echo "Resolving alias:      $1 -> ${BASH_ALIASES[$1]}"
            re="[[:space:]]+"
            force_file=1
            if [[ "${BASH_ALIASES[$1]}" =~ $re ]]; then
              array=(${BASH_ALIASES[$1]})
              _clevertype ${array[0]}
              echo "$input is ${BASH_ALIASES[$1]}"
            else
              _clevertype ${BASH_ALIASES[$1]}
            fi

            
        else
            type "$1"
        fi
    }
    _clevertype "$1"
}

This gives the following output:

$ clevertype /usr/bin/python3.13
/usr/bin/python3.13 is /usr/bin/python3.13

$ clevertype python3.13
Resolving from PATH:  python3.13 -> /usr/bin/python3.13
python3.13 is /usr/bin/python3.13

$ clevertype python
Resolving from PATH:  python -> /usr/bin/python
Resolving links:      /usr/bin/python -> /usr/bin/python3.13
python is /usr/bin/python3.13

$ clevertype /usr/bin/python3
Resolving links:      /usr/bin/python3 -> /usr/bin/python3.13
/usr/bin/python3 is /usr/bin/python3.13

$ clevertype mypython
Resolving alias:      mypython -> python
Resolving from PATH:  python -> /usr/bin/python
Resolving links:      /usr/bin/python -> /usr/bin/python3.13
mypython is /usr/bin/python3.13

$ clevertype alias1
Resolving alias:      alias1 -> alias2
Resolving alias:      alias2 -> alias3
Resolving alias:      alias3 -> /usr/bin/python
Resolving links:      /usr/bin/python -> /usr/bin/python3.13
alias1 is /usr/bin/python3.13

$ clevertype myfunction
myfunction is a function
myfunction () 
{ 
    echo hello
}

$ clevertype type
type is a shell builtin

$ clevertype mytype
Resolving alias:      mytype -> type
type is a shell builtin

$ clevertype ls
Resolving alias:      ls -> ls --color=auto
Resolving from PATH:  ls -> /usr/bin/ls
ls is /usr/bin/ls
ls is ls --color=auto

There might be pathological cases where it does not work, since it tries to mimic complex behaviors from bash and the the operating system. For instance, "ls" is often an alias to "ls [options]", like can be seen in the last example. It might not work if the command has spaces.

3
  • readlink resolves all links in one go. I wonder if the is a way to resolve only one link at a time. Commented Mar 12 at 20:36
  • As far as I can tell, readlink reads the current symlink and doesn't follow it. realpath does resolve it all the way down to the final filename. Commented Mar 12 at 22:06
  • In this context, I need to absolute path, so I use "readlink -e", which follows all links. I don't see a way to get an absolute path while resolving only one level of linking. Commented Mar 13 at 6:46
0

You could construct this yourself: you could use readlink or realpath to resolve the links recursively.

Putting it all together in a Bash function could be something like this:

clevertype ()
{
    if [[ "$(type -t "$1")" == "file" ]]; then
        echo "$1 resolves to $(readlink -e "$(type -p "$1")")"
    else
        type "$1"
    fi
}
0

This is my take on what you are asking for:

llt () 
{ 
    [[ -n $1 ]] || return 1;
    printf -v filetype '%s' $(type -ta $1);
    echo "";
    case $filetype in 
        "")
            echo "llt: $1: not found" 1>&2;
            return 1
        ;;
        *alias*)
            echo "Alias";
            alias "$1" | awk '{sub(/^alias /, ""); print}';
            echo ""
        ;;&
        *function*)
            echo "Shell Function";
            declare -f "$1";
            echo ""
        ;;&
        *builtin*)
            echo "Shell Builtin";
            echo ""
        ;;&
        *keyword*)
            echo "Shell Reserved Word";
            echo ""
        ;;&
        *file*)
            echo "File";
            for fn in $(type -ap $1);
            do
                stat $fn | awk 'NR==1{sub(/^  File: /, ""); print}';
            done
        ;;
    esac
}

On my Mac, llt ls outputs:

Alias
ls='/usr/local/bin/eza'

File
/usr/local/gnubin/ls -> /usr/local/Cellar/coreutils/9.6/libexec/gnubin//ls
/bin/ls

The initial printf will create a one liner based on the type -ta $1 ie aliasfilefile Each test of the case statement will perform the test and the ;;& tells the case statement to continue onto the next test (so the example will hit the alias test and the file test.

Forgot to mention if the command is an alias or function, I output the contents of that alias or function

3
  • you might want to add a link to the source of those prt* functions, or just replace them with standard ones Commented Mar 12 at 20:54
  • Thanks for the suggestion, also added a sample output Commented Mar 13 at 1:12
  • Thanks. I created a directory containing a Bash script called "mytool" and in the same directory a link "mytoollink" to "mytool". The output is "./mytoollink -> mytool", it should be /fullpath/mytool. In addition, I do not see how the code can handle resolution of multiple layers (links of links, aliases of links...) Commented Mar 13 at 6:59

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.