ChatGPT解决这个技术问题 Extra ChatGPT

What is the difference between "#!/usr/bin/env bash" and "#!/usr/bin/bash"?

In the header of a Bash script, what's the difference between those two statements:

#!/usr/bin/env bash #!/usr/bin/bash

When I consulted the env man page, I get this definition:

 env - run a program in a modified environment

What does it mean?

Who can tell me why this question is closed, " related to programming or software development" isen't ?
This question should not have been marked as off-topic. It just needs 5 people with above a score of 3000 to mark it as "on-topic" and it can be reopened. It is a question - specifically about programming.
I'm shocked. Shocked to find that Linux documentation is rife with tautologies. xkcd.com/703 git-man-page-generator.lokaltog.net
One major difference is that, depending on the system, #!/usr/bin/bash won't work. On my system (Ubuntu 17.04), bash is /bin/bash, and there is no /usr/bin/bash. The distinction between /bin and /usr/bin is largely arbitrary. And just to add to the frivolity, most systems put the env command in /usr/bin, but that's not guaranteed.

u
user541686

Running a command through /usr/bin/env has the benefit of looking for whatever the default version of the program is in your current environment.

This way, you don't have to look for it in a specific place on the system, as those paths may be in different locations on different systems. As long as it's in your path, it will find it.

One downside is that you will be unable to pass more than one argument (e.g. you will be unable to write /usr/bin/env awk -f) if you wish to support Linux, as POSIX is vague on how the line is to be interpreted, and Linux interprets everything after the first space to denote a single argument. You can use /usr/bin/env -S on some versions of env to get around this, but then the script will become even less portable and break on fairly recent systems (e.g. even Ubuntu 16.04 if not later).

Another downside is that since you aren't calling an explicit executable, it's got the potential for mistakes, and on multiuser systems security problems (if someone managed to get their executable called bash in your path, for example).

#!/usr/bin/env bash #lends you some flexibility on different systems
#!/usr/bin/bash     #gives you explicit control on a given system of what executable is called

In some situations, the first may be preferred (like running python scripts with multiple versions of python, without having to rework the executable line). But in situations where security is the focus, the latter would be preferred, as it limits code injection possibilities.


Another drawback is that you can't pass an additional argument to the interpreter.
@KeithThompson : Incorrect info.. You can pass options to the underlying interpreter using /usr/bin/env!
@GauravAgarwal: Not on my system. A script containing just this single line: #!/usr/bin/env echo Hello complains: /usr/bin/env: echo Hello: No such file or directory. Apparently it treats echo Hello as a single argument to /usr/bin/env.
@AndréLaszlo: The env command certainly allows arguments to be passed to the command. The issue is the semantics of the #! line, and that depends on the kernel. Recent Linux kernels do allow things like #!/usr/bin/env command args, but older Linux kernels and other systems don't.
Why are there backticks in the code block? Shouldn't they be removed?
D
Dolphiniac

Using #!/usr/bin/env NAME makes the shell search for the first match of NAME in the $PATH environment variable. It can be useful if you aren't aware of the absolute path or don't want to search for it.


At least you have to know where env is :).
Excellent answer. Explains succinctly what the env shebang does, rather than saying "chooses the program based on your system configuration"
Here is the situation I encountered. When I run the command "./script.sh" with "#!/usr/bin/env bash" in the first line of script.sh, it gives a feedback of "-bash: ./tools/dist_train.sh: Permission denied". When I run the command "bash ./script.sh". It can work well. Could you tell me why "#!/usr/bin/env NAME" doesn't work...?
@EricKani: Most likely your "./script.sh" does have its executable flag unchecked (so you can't execute it on its own). "bash ./script.sh" happily accepts non-executable script files. (Solution: chmod +x ./script.sh)
M
Mecki

If the shell scripts start with #!/bin/bash, they will always run with bash from /bin. If they however start with #!/usr/bin/env bash, they will search for bash in $PATH and then start with the first one they can find.

Why would this be useful? Assume you want to run bash scripts, that require bash 4.x or newer, yet your system only has bash 3.x installed and currently your distribution doesn't offer a newer version or you are no administrator and cannot change what is installed on that system.

Of course, you can download bash source code and build your own bash from scratch, placing it to ~/bin for example. And you can also modify your $PATH variable in your .bash_profile file to include ~/bin as the first entry (PATH=$HOME/bin:$PATH as ~ will not expand in $PATH). If you now call bash, the shell will first look for it in $PATH in order, so it starts with ~/bin, where it will find your bash. Same thing happens if scripts search for bash using #!/usr/bin/env bash, so these scripts would now be working on your system using your custom bash build.

One downside is, that this can lead to unexpected behavior, e.g. same script on the same machine may run with different interpreters for different environments or users with different search paths, causing all kind of headaches.

The biggest downside with env is that some systems will only allow one argument, so you cannot do this #!/usr/bin/env <interpreter> <arg>, as the systems will see <interpreter> <arg> as one argument (they will treat it as if the expression was quoted) and thus env will search for an interpreter named <interpreter> <arg>. Note that this is not a problem of the env command itself, which always allowed multiple parameters to be passed through but with the shebang parser of the system that parses this line before even calling env. Meanwhile this has been fixed on most systems but if your script wants to be ultra portable, you cannot rely that this has been fixed on the system you will be running.

It can even have security implications, e.g. if sudo was not configured to clean environment or $PATH was excluded from clean up. Let me demonstrate this:

Usually /bin is a well protected place, only root is able to change anything there. Your home directory is not, though, any program you run is able to make changes to it. That means malicious code could place a fake bash into some hidden directory, modify your .bash_profile to include that directory in your $PATH, so all scripts using #!/usr/bin/env bash will end up running with that fake bash. If sudo keeps $PATH, you are in big trouble.

E.g. consider a tool creates a file ~/.evil/bash with the following content:

#!/bin/bash

if [ $EUID -eq 0 ]; then
  echo "All your base are belong to us..."
  # We are root - do whatever you want to do
fi

/bin/bash "$@"

Let's make a simple script sample.sh:

#!/usr/bin/env bash

echo "Hello World"

Proof of concept (on a system where sudo keeps $PATH):

$ ./sample.sh
Hello World

$ sudo ./sample.sh
Hello World

$ export PATH="$HOME/.evil:$PATH"

$ ./sample.sh
Hello World

$ sudo ./sample.sh
All your base are belong to us...
Hello World

Usually the classic shells should all be located in /bin and if you don't want to place them there for whatever reason, it's really not an issue to place a symlink in /bin that points to their real locations (or maybe /bin itself is a symlink), so I would always go with #!/bin/sh and #!/bin/bash. There's just too much that would break if these wouldn't work anymore. It's not that POSIX would require these position (POSIX does not standardize path names and thus it doesn't even standardize the shebang feature at all) but they are so common, that even if a system would not offer a /bin/sh, it would probably still understand #!/bin/sh and know what to do with it and may it only be for compatibility with existing code.

But for more modern, non standard, optional interpreters like Perl, PHP, Python, or Ruby, it's not really specified anywhere where they should be located. They may be in /usr/bin but they may as well be in /usr/local/bin or in a completely different hierarchy branch (/opt/..., /Applications/..., etc.). That's why these often use the #!/usr/bin/env xxx shebang syntax.


S
Salah Eddine Taouririt

Instead of explicitly defining the path to the interpreter as in /usr/bin/bash/, by using the env command, the interpreter is searched for and launched from wherever it is first found. This has both upsides and downsides


On most systems, they will be functionally the same, but it depends on the location of your bash and env executables. Not sure how this will affect environment variables, though.
"It is possible to specify the interpreter without using env, by giving the full path to the interpreter. A problem is that on different computer systems, the exact path may be different. By instead using env, the interpreter is searched for and located at the time the script is run. This makes the script more portable, but also increases the risk that the wrong interpreter is selected because it searches for a match in every directory on the executable search path. It also suffers from the same problem in that the path to the env binary may also be different on a per-machine basis."-Wikipedia
s
sudo97

I find it useful, because when I didn't know about env, before I started to write script I was doing this:

type nodejs > scriptname.js #or any other environment

and then I was modifying that line in the file into shebang.
I was doing this, because I didn't always remember where is nodejs on my computer -- /usr/bin/ or /bin/, so for me env is very useful. Maybe there are details with this, but this is my reason