By "simplified," we mean that we will implement only the part
that checks all the directories in your PATH for the command
you give as argument (we won't implement checking for aliases,
built-in commands, etc.).
We can do this by picking off the directories in PATH one by
one, using one of the shell's pattern-matching operators, and
seeing if there is a file with the given name in the directory that you have permission to execute. Here is the code:
path=$PATH:
dir=${path%%:*}
while [[ -n $path ]]; do
if [[ $dir == "" ]] ; then
dir="."
fi
if [[ -x $dir/$1 && ! -d $dir/$1 ]]; then
print "$dir/$1"
exit 0
fi
path=${path#*:}
dir=${path%%:*}
done
exit 1
The next line picks the first directory off $path
by using the operator that deletes the longest match of the pattern
given. In this case, we delete the longest match of the pattern
:*, i.e., a colon followed by anything. This gives us the first
directory in $path, which we store in the variable dir.
The condition in the while loop checks if
$path is non-null. If it is not null,
it first checks that $dir is not null.
This could happen for two adjacent colons, which represent the
current directory. In this case, dir is
explicitly set to dot.
Next, the script
constructs
the full pathname $dir/$1 and sees
if there is a file by that name for which you have execute permission
(and that is not a directory). If so, it prints the full pathname and
exits the routine with a 0 ("OK") exit status.
If a file is not found, this code is run:
path=${path#*:}
dir=${path%%:*}
The first of these uses another shell string operator: this one
deletes the shortest match of the pattern given
from the front of the string. By now, this type of operator should
be familiar. This line deletes the front directory from $path
and assigns the result back to path.
The second line is the same as before the while: it finds
the (new) front directory in $path and assigns it to dir.
This sets up the loop for another iteration.
If filename is an executable program (compiled from C or some other
language), typing file filename
produces output similar to this:
filename: ELF 32-bit LSB executable 80386 Version 1
However, if filename is not an executable program, it will examine
the first few lines and try to guess what kind of information the
file contains.
If the file contains text (as opposed to binary data),
file will look for indications that it is English, shell
commands, C, Fortran, troff(1) input, and various other things.
file is wrong sometimes, but it is usually correct.
Assume that fred is an executable file in the directory
/usr/bin, and that bob is a shell script
in /usr/local/bin.
Typing file /usr/bin/fred produces this output:
/usr/bin/fred: ELF 32-bit LSB executable 80386 Version 1
Typing file /usr/local/bin/bob has this result:
/usr/local/bin/bob: commands text
We can just substitute file for print
to print a more informative message in our script:
path=$PATH:
while [[ -n $path ]]; do
dir=${path%%:*}
if [[ $dir == "" ]] ; then
dir="."
fi
if [[ -x $dir/$1 && ! -d $dir/$1 ]]; then
file $dir/$1
exit 0
fi
path=${path#*:}
done
exit 1