Why “which” returns none on some commands I can run? [duplicate]

I found which alias or which compgen etc returns nothing.

Both of those are shell builtins. which knows nothing of shell builtins: it simply searches a path for executables.

For a more reliable result, use type.

$ type compgen
compgen is a shell builtin
$ type alias
alias is a shell builtin

type has better knowledge of what executes because it is a shell builtin.

Why which is unreliable

which often gives the wrong answer. Observe:

$ type pwd
pwd is a shell builtin
$ which pwd
/bin/pwd

When you run pwd, without specifying an explicit path, the shell will execute its builtin, not the executable that which found.

Here are more examples of which giving the wrong answer:

$ type echo
echo is a shell builtin
$ which echo
/bin/echo
$ type [
[ is a shell builtin
$ which [
/usr/bin/[

Observe:

$ type /bin/echo
/bin/echo is /bin/echo

When you give an explicit path for echo, such as /bin/echo, then the shell would run that executable, not its builtin. type knows that also, as you can see above.

How which works internally

On debian-like systems, which is a simple shell script, The relevant part of which is:

   for ELEMENT in $PATH; do
    if [ -z "$ELEMENT" ]; then
     ELEMENT=.
    fi
    if [ -f "$ELEMENT/$PROGRAM" ] && [ -x "$ELEMENT/$PROGRAM" ]; then
     puts "$ELEMENT/$PROGRAM"
     RET=0
     [ "$ALLMATCHES" -eq 1 ] || break
    fi
   done

As you can see, it does a simple search along the PATH for an executable file of the name given.