zsh
is one of the few shells (the other ones being tcsh
(which
originated as a csh
script for csh
users, which also had its limitation, tcsh
made it a builtin as an improvement)) where which
does something sensible since it's a shell builtin, but somehow you or your OS (via some rc
file) broke it by replacing it with a call to the system which
command which can't do anything sensible reliably since it doesn't have access to the interns of the shell so can't know how that shell interprets a command name.
In zsh, all of which
, type
, whence
and where
are builtin commands that are all used to find out about what commands are, but with different outputs. They're all there for historical reason, you can get all of their behaviours with different flags to the whence
command.
You can get the details of what each does by running:
info -f zsh --index-search=which
Or type info zsh
, then bring up the index with i
, and enter the builtin name (completion is available).
And avoid using /usr/bin/which
. There's no shell nowadays where that which
is needed. As Timothy says, use the builtin that your shell provides for that. Most POSIX shells will have the type
command, and you can use command -v
to only get the path of a command (though both type
and command -v
are optional in POSIX (but not Unix, and not any longer in LSB), they are available in most if not all the Bourne-like shells you're likely to ever come across).
(BTW, it looks like /usr/bin
appears twice in your $PATH
, you could add a typeset -U path
to your ~/.zshrc
)
That depends on the system you are running. One many OSes, especially Linux based ones, sh
is a link to bash
.
In such case, there are still some differences in behavior where bash
try to be more like traditional bourne shell when called sh
, but it still accepts most bashisms.
On some other OSes, like Debian based ones, sh
is provided by dash
, not bash
. That makes a much bigger difference as dash
doesn't support bashisms, being designed to be a clean POSIX shell implementation.
On proprietary OSes, sh
is often either provided by a POSIX compliant ksh88
which like dash
, doesn't implement bashisms. On Solaris 10 and older, depending on what your PATH is, sh
will likely be a legacy Bourne shell, predating POSIX.
In any case, you likely got the same output with your test simply because your script was not using any bash
specific command, option or syntax.
When you run ./executable
, what shell will be run essentially depends on the shebang written at the beginning of the .executable script. That will be bash
if the shebang specifies it:
#!/bin/bash
....
If there is no shebang and you call the script from a POSIX compliant shell, the script should technically be executed by the first sh
found in the PATH. Many shell interpreters like bash
, dash
and ksh
are considering themselves to be POSIX so will interpret the script. Note that the SHELL
environment variable is not used here.
Best Answer
eval
andexec
are completely different beasts. (Apart from the fact that both will run commands, but so does everything you do in a shell.)What
exec cmd
does, is exactly the same as just runningcmd
, except that the current shell is replaced with the command, instead of a separate process being run. Internally, running say/bin/ls
will callfork()
to create a child process, and thenexec()
in the child to execute/bin/ls
.exec /bin/ls
on the other hand will not fork, but just replaces the shell.Compare:
with
echo $$
prints the PID of the shell I started, and listing/proc/self
gives us the PID of thels
that was ran from the shell. Usually, the process IDs are different, but withexec
the shell andls
have the same process ID. Also, the command followingexec
didn't run, since the shell was replaced.On the other hand:
eval
will run the arguments as a command in the current shell. In other wordseval foo bar
is the same as justfoo bar
. But variables will be expanded before executing, so we can execute commands saved in shell variables:It will not create a child process, so the variable is set in the current shell. (Of course
eval /bin/ls
will create a child process, the same way a plain old/bin/ls
would.)Or we could have a command that outputs shell commands. Running
ssh-agent
starts the agent in the background, and outputs a bunch of variable assignments, which could be set in the current shell and used by child processes (thessh
commands you would run). Hencessh-agent
can be started with:And the current shell will get the variables for other commands to inherit.
Of course, if the variable
cmd
happened to contain something likerm -rf $HOME
, then runningeval "$cmd"
would not be something you'd want to do. Even things like command substitutions inside the string would be processed, so one should really be sure that the input toeval
is safe before using it.Often, it's possible to avoid
eval
and avoid even accidentally mixing code and data in the wrong way.