If I want to return the path of a given executable, I can run:
which mysql
Which returns for example:
/usr/bin/mysql
I'd like to return only:
/usr/bin
How can I do that?
If I want to return the path of a given executable, I can run:
which mysql
Which returns for example:
/usr/bin/mysql
I'd like to return only:
/usr/bin
How can I do that?
executable=mysql
executable_path=$(command -v -- "$executable") &&
dirname -- "$executable_path"
Of course, that won't work if $executable
is a shell builtin, function or alias. I'm not aware of any shell where mysql
is a builtin. It won't be a function or alias unless you defined them earlier, but then you should know about it. An exception to that could be bash
which supports exported functions.
$ bash -c 'command -v mysql'
/usr/bin/mysql
$ mysql='() { echo test;}' bash -c 'command -v mysql'
mysql
In general this command should be avoided.
Why?
Because it uses your current environment when constructing the $PATH
that it's going to evaluate when looking for executables. This can lead to confusion when dealing with shell scripts and such, that will potentially be using the $PATH
as constructed by your ~/.bashrc
and ~/.bash_profile
files when executed.
For example:
# path to start
$ echo $PATH
/usr/kerberos/bin:/usr/local/bin:/bin:/usr/bin:/home/sam/bin
# let's change it
$ export PATH=/usr/kerberos/bin:/usr/local/bin:/bin:/usr/bin
$ echo $PATH
/usr/kerberos/bin:/usr/local/bin:/bin:/usr/bin
# simulate a new env. for a shell script
$ bash -lc "echo \$PATH"
/usr/kerberos/bin:/usr/local/bin:/bin:/usr/bin:/home/sam/bin
Notice that the shell script if executed would have the original environment, not the current one!
You can also use the command type
to locate executables within your environment:
$ type ls
ls is aliased to `ls --color=auto'
Or to see everything, use the -a
switch:
$ type -a ls
ls is aliased to `ls --color=auto'
ls is /bin/ls
type
will show you aliases as well as the location. For finding the directory though, @StephaneChazelas solution of using command
is best.
You can also use the command whereis
but this doesn't use your environment, it has a hardcoded set of directories that it looks in. See man whereis for more details.
This one might confuse people but some distros include a tool that runs from a cron job, typically daily, that scans the entire disk and builds a little database file which you can then search using the locate
command.
/etc/cron.daily/mlocate.cron
on my Fedora 14 system. /usr/bin/updatedb
./var/lib/mlocate/mlocate.db
. Using what I'd consider a bit of a hack you can coax locate
to only look for files that are named ls
as follows:
$ locate -b '\ls'
/bin/ls
/usr/share/javadoc/java-1.6.0-openjdk/api/org/w3c/dom/ls
/usr/share/javadoc/java-1.6.0-openjdk/jre/api/plugin/dom/org/w3c/dom/ls
/usr/share/locale/l10n/ls
/var/cache/abrt-di/usr/src/debug/gcc-4.5.1-20100924/libjava/classpath/external/w3c_dom/org/w3c/dom/ls
/var/cache/abrt-di/usr/src/debug/gcc-4.5.1-20100924/libjava/classpath/gnu/xml/dom/ls
The above works because you're \
is a globbing character, so this disables the implicit replacement of ls
with *ls*
.
See the locate man page for more details.
A lesser known tool is called whatis
. This one is similar to locate
, so it's backed by a DB. It is useful for finding out commands however:
$ whatis ls
ls (1) - list directory contents
ls (1p) - list directory contents
The tool is invoked from a cron job, /etc/cron.daily/makewhatis.cron
. However, makewhatis
builds its database from the man
pages on your system.
You can see more about it in the makewhatis man pages.
You can use the command dirpath
against any of the output produced by the commands I mentioned above to get just the directory component.
dirpath
does no checking so you can give it just about any type of path to a file and it will give you the directory piece:
$ dirname /some/path/to/a/script
/some/path/to/a
As others have explained, don't use which
.
You can write a little shell function that iterates over the PATH
variable.
which-directory () (
IFS=:
set -g
for d in $PATH; do
if [ -z "$d" ]; then d=.; fi
if [ -x "$d/$1" ]; then printf '%s\n' "$d"; return; fi
done
return 1
)
Alternatively, you can call command -v
. This reports aliases and functions as well as external commands. In case you have an alias or function mysql
that calls the external command of the same name with additional parameters, you can call command -v
in a newly-started shell that doesn't have any functions or aliases defined. Note that this does the right thing only if the alias or function calls the external command of the same name. The following two one-liners are equivalent (assuming you have no directory or executable file name on your PATH containing a newline).
dirname -- "$(sh -c 'command -v "$0"' -- mysql)"
sh -c 'command -v "$0"' -- mysql | sed -e 's!.*/!!'
In zsh, this one-liner does the same thing:
echo =mysql(:h)
dirname is simplest way of achieving this:
$ which mysql
/usr/bin/mysql
$ dirname `which mysql`
/usr/bin
which
. – user Aug 01 '13 at 15:04which
is bad in my answer. Let me know what you think. – slm Aug 01 '13 at 15:47.
. (Because there's an alias, whichwhich
isn't aware of.) Zsh has the very convenient=mysql
syntax, but unfortunately it doesn't stack with${…##*/}
or…:h
. – Gilles 'SO- stop being evil' Aug 01 '13 at 23:59${${:-mysql}:c:h}
though. ksh and zsh havewhence -p
to return paths of commands even if there's a builtin/alias/function for it, but then that might not be what you want as it's not what the shell would invoke. – Stéphane Chazelas Aug 02 '13 at 06:59:c
, nice. But actually I made a mistake,=mysql
does stack with glob qualifiers. An alias or function could of course do anything, but having an alias that calls the command with the same name with additional parameters is very common. – Gilles 'SO- stop being evil' Aug 02 '13 at 07:31