The difference between [
and [[
is quite fundamental.
[
is a command. Its arguments are processed just the way any other commands arguments are processed. For example, consider:
[ -z $name ]
The shell will expand $name
and perform both word splitting and filename generation on the result, just as it would for any other command.
As an example, the following will fail:
$ name="here and there"
$ [ -n $name ] && echo not empty
bash: [: too many arguments
To have this work correctly, quotes are necessary:
$ [ -n "$name" ] && echo not empty
not empty
[[
is a shell keyword and its arguments are processed according to special rules. For example, consider:
[[ -z $name ]]
The shell will expand $name
but, unlike any other command, it will perform neither word splitting nor filename generation on the result. For example, the following will succeed despite the spaces embedded in name
:
$ name="here and there"
$ [[ -n $name ]] && echo not empty
not empty
Summary
[
is a command and is subject to the same rules as all other commands that the shell executes.
Because [[
is a keyword, not a command, however, the shell treats it specially and it operates under very different rules.
Your
# If the current (login) shell is Bash, then
if [ "${BASH_VERSION:-}" ]; then
# source ~/.bashrc if it exists.
if [ -f "$HOME/.bashrc" ]; then
. "$HOME/.bashrc"
fi
fi
code is completely POSIX conformant and the best way to check that you're currently running bash
. Of course the $BASH_VERSION
variable is bash-specific, but that's specifically why you're using it! To check that you're running bash
!
Note that $BASH_VERSION
will be set whether bash
is invoked as bash
or sh
. Once you've asserted that you're running bash
, you can use [ -o posix ]
as an indicator that the shell was invoked as sh
(though that option is also set when POSIXLY_CORRECT is in the environment or bash
is called with -o posix
, or with SHELLOPTS=posix in the environment. But in all those cases, bash
will behave as if called as sh
).
Another variable you could use instead of $BASH_VERSION
and that checkbashism
doesn't seem to complain about unless passed the -x
option is $BASH
. That is also specific to bash
so one you should also be able to use to determine whether you're running bash
or not.
I'd also argue it's not really a proper use of checkbashisms
. checkbashisms
is a tool to help you write portable sh
scripts (as per the sh
specification in the Debian policy, a superset of POSIX), it helps identify non-standard syntax introduced by people writing scripts on systems where sh
is a symlink to bash
.
A .profile
is interpreted by different shells, many of which are not POSIX compliant. Generally, you don't use sh
as your login shell, but shells like zsh
, fish
or bash
with more advanced interactive features.
bash
and zsh
, when not called as sh
and when their respective profile session file (.bash_profile
, .zprofile
) are not POSIX conformant (especially zsh
) but still read .profile
.
So it's not POSIX syntax you want for .profile
but a syntax that is compatible with POSIX (for sh
), bash
and zsh
if you're ever to use those shells (possibly even Bourne as the Bourne shell also reads .profile
but is not commonly found on Linux-based systems).
checkbashisms
would definitely help you find out bashisms but may not point out POSIX syntax that is not compatible with zsh
or bash
.
Here, if you want to use bash
-specific code (like the work around of that bash
bug whereby it doesn't read ~/.bashrc
in interactive login shells), a better approach would be to have ~/.bash_profile
do that (before or after sourcing ~/.profile
where you put your common session initialisations).
Best Answer
Assuming
env
is in your path:env
runs the executable file named by its first argument in a (possibly) modified environment; as such, it does not know about or work with shell built-in commands.This produces some shell job control cruft, but doesn't rely on an external command:
exec
requires an executable to replace the current shell, so doesn't use any built-ins. The job is run in the background so that you replace the forked background shell, not your current shell.