You need to execute, source, or read a file, but it may be located in a number of different places in or outside of the $PATH.
If you are going to source the file and it’s located somewhere on the $PATH, just source it.
bash’s built-in source command (also known by the shorter-to-type but harder-to-read POSIX name “.”) will search the $PATH if the sourcepath shell option is set, which it is by default:
1 |
$ source myfile |
If you want to execute a file only if you know it exists in the $PATH and is executable, and you have bash version 2.05b or higher, use type -P to search the $PATH.
Unlike the which command, type -P only produces output when it finds the file, which makes it much easier to use in this case:
1 2 3 4 5 6 7 8 9 |
LS=$(type -P ls) [ -x $LS ] && $LS # --OR-- LS=$(type -P ls) if [ -x $LS ]; then : commands involving $LS here fi |
If you need to look in a variety of locations, possibly including the $PATH, use a for loop.
To search the $PATH, use the variable substitution operator ${variable/pattern/ replacement} to replace the : separator with a space, and then use for as usual.
To search the $PATH and other possible locations, just list them:
1 2 3 4 5 6 7 8 9 |
for path in ${PATH//:/ }; do [ -x "$path/ls" ] && $path/ls done # --OR-- for path in ${PATH//:/ } /opt/foo/bin /opt/bar/bin; do [ done |
If the file is not in the $PATH, but could be in a list of locations, possibly even under different names, list the entire path and name:
1 2 3 |
for file in /usr/local/bin/inputrc /etc/inputrc ~/.inputrc; do [ -f "$file" ] && bind -f "$file" && break # Use the first one found done |
Perform any additional tests as needed.
For example, you may wish to use screen when logging in if it’s present on the system:
1 2 3 4 5 6 7 8 |
for path in ${PATH//:/ }; do if [ -x "$path/screen" ]; then # If screen(1) exists and is executable: for file in /opt/bin/settings/run_screen ~/settings/run_screen; do [ -x "$file" ] && $file && break # Execute the first one found done fi done |
Using for to iterate through each possible location may seem like overkill, but it’s actually very flexible and allows you to search wherever you need to, apply whatever other tests are appropriate, and then do whatever you want with the file if found.
By replacing : with a space in the $PATH, we turn it into the kind of space-delimited list for expects (but as we also saw, any space delimited list will work).
Adapting this technique as needed will allow you to write some very flexible and portable shell
scripts that can be very tolerant of file locations.
You may be tempted to set $IFS=’:’ to directly parse the $PATH, rather than preparsing it into $path.
That will work, but involves extra work with variables and isn’t as flexible.
You may also be tempted to do something like the following:
1 |
[ "$(which myfile)" ] && bind -f $(which myfile) |
The problem here is not when the file exists, but when it doesn’t. The which utility behaves differently on different systems.
The Red Hat which is also aliased to provide details when the argument is an alias, and to set various command-line switches; and it returns a not found message (while which on Debian or FreeBSD do
not).
But if you try that line on NetBSD you could end up trying to bind no myfile in /sbin /usr/sbin /bin /usr/bin /usr/pkg/sbin /usr/pkg/bin /usr/X11R6/bin /usr/ local/sbin /usr/local/bin, which is not what you meant.
The command command is also interesting in this context. It’s been around longer than type -P and may be useful under some circumstances.
Red Hat Enterprise Linux 4.x behaves like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
$ alias which alias which='alias | /usr/bin/which --tty-only --read-alias --show-dot --show-tilde' $ which rd alias rd='rmdir' /bin/rmdir $ which ls alias ls='ls --color=auto -F -h' /bin/ls $ which cat /bin/cat $ which cattt /usr/bin/which: no cattt in (/usr/kerberos/bin:/usr/local/bin:/bin:/usr/bin:/usr/ X11R6/bin:/home/jp/bin) $ command -v rd alias rd='rmdir' $ command -v ls alias ls='ls --color=auto -F -h' $ command -v cat /bin/cat |
Debian and FreeBSD (but not NetBSD or OpenBSD) behave like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
$ alias which -bash3: alias: which: not found $ which rd $ which ls /bin/ls $ which cat /bin/cat $ which cattt $ command -v rd -bash: command: rd: not found $ command -v ls /bin/ls $ command -v cat /bin/cat $ command -v ll alias ll='ls -l' |