I tried the following but it doesn't seem to work:
$ cat script.sh
#!/bin/env -i /bin/sh
/bin/env
$ script.sh
/bin/env: invalid option -- ' '
Try `/bin/env --help' for more information.
I tried the following but it doesn't seem to work:
$ cat script.sh
#!/bin/env -i /bin/sh
/bin/env
$ script.sh
/bin/env: invalid option -- ' '
Try `/bin/env --help' for more information.
The reason this does not work is because it sees -i /bin/sh
as a single argument to env
. Usually this would be 2 arguments, -i
and /bin/sh
. This is just a limitation of the shebang. No way around it.
However you can still perform this task, just a different way.
If you want this task to be performed by the script itself, and not have to do something like env -i script.sh
, you can have the script re-exec itself.
#!/bin/sh
[ -z "$CLEANED" ] && exec /bin/env -i CLEANED=1 /bin/sh "$0" "$@"
This will cause the script to re-exec itself if the CLEANED
environment variable isn't set. Then on re-exec, it sets the variable to make sure that it doesn't go into a loop.
env
from the GNU coreutils now have an option -S
to workaround issues similar to this one but not exactly the same. For example #!/usr/bin/env -S perl -T
.
– Weijun Zhou
Nov 22 '18 at 17:54
#!/usr/bin/env -S -i /bin/sh
. Be aware of portability issues.
– Weijun Zhou
Nov 22 '18 at 18:07
[ -z "${CLEANED:-}" ]"
when -u
is set in the shebang e.g #!/bin/sh -eu
– Ruben Jenster
Dec 14 '23 at 17:32
Run your script with env -i
:
env -i script.sh
And the script as usual:
#!/bin/sh
# ... your code here
If you mean to run with a clean environment without explicitly say that when you run. Eduardo Ivanec gives some ideas in this answer, you can recursively call your script with exec
when the environment is not clean (e.g. $HOME is defined):
[ "$HOME" != "" ] && exec -c $0
With bash, you can do it like this:
#!/usr/bin/bash
set -e
set -u
[ -v HOME ] && exec -c "$0" "$@"
# continue with the rest of the script
# e.g. print the cleaned environment:
export
The set -e
and set -u
commands are not strictly necessary, but I include them to demonstrate that this approach doesn't rely on accessing unset variables (as e.g. [ "$HOME" != "" ]
would) and is compatible with a set -e
setting.
Testing the HOME
variable should be safe because bash executes scripts in non-interactive mode, i.e. configuration files like ~/.bashrc
(where environment variables may be set) are not sourced during startup.
Example output:
declare -x OLDPWD
declare -x PWD="/home/juser"
declare -x SHLVL="1"
The Linux 2-argument shebang limitation (interpeter + single argument) is noted in most of the answers, but to say this cannot be done is incorrect — you just need to change to an interpreter that can do something useful with a single argument:
#!/usr/bin/perl -we%ENV=();exec "/bin/sh " . join " ", map "'$_'", @ARGV;
# your sh script here
What this does is invoke perl
with a one-liner script (-e
) that clears %ENV
(cheaper than env -i
) and invokes exec /bin/sh
, correctly quoting the arguments. Further perl
logic can be added, if required (though not much on Linux since you're limited to BINPRM_BUF_SIZE
characters, which is likely 128). There's a variation on that using awk
in this answer to the question Multiple arguments in shebang
Sadly, this is Linux specific, it will not work on a system which allows multiple shebang arguments :-/
perl
processes this string as a single argument, so it's not quoted above as you would ordinarily do with perl -e ...
from the command line (if you were to add quotes these are preserved, perl sees only a literal string, with warnings on it will complain about a useless constant).
Also note there's a slight change in behaviour when used this way, @ARGV
normally contains only the arguments, and $0
contains the script name, but with this shebang $ARGV[0]
is the name of the script which makes creating the exec()
arguments a little easier (and $0
contains -e
).
You can also solve this with an interpreter that "reprocesses" its command line (without an extra -c
argument) the ancient AT&T ksh93
does:
#!/bin/ksh /usr/bin/env -i /bin/sh
though perhaps ksh
isn't as common nowadays ;-)
(bash
has a similar feature with --wordexp
, but it's "undocumented" in the versions where it works, and not enabled at compile time in the versions where it is documented :-/ It also can't be used for this since it needs two arguments...)
Also, effectively a variation on @Patrick and @maxschlepzig's answers:
#!/bin/bash
[ "$_" != bash ] && exec -c -a "bash" /bin/bash "$0" "$@"
# your script here
Rather than use a new variable this uses the special "_
" variable, if it's not set to exactly "bash" then replace the script with exec
using -a
to make ARGV[0]
(and hence $_
) just "bash", and using -c
to clear the environment.
Alternatively, if it is acceptable to clean the environment at at the start of the script (bash only):
#!/bin/sh
unset $(compgen -e)
# your script here
That uses compgen
(completion helper) to list the names of all the exported environment variables, and unset
s them in one go.
See also Multiple arguments in shebang for more details on the general problem of shebang behaviour.