Yes, _
is an environment variable of the new Bash shell; you can see that by running
tr '\0' '\n' < /proc/$$/environ | grep _=
inside the shell: that shows the contents of the shell’s initial environment. You won’t see it in the first shell because there wasn’t a previous shell to set it before it started.
Expanding $_
inside Bash refers to the _
special parameter, which expands to the last argument of the previous command. (Internally Bash handles this by using a _
shell variable, which is updated every time a command is parsed, but that’s really an implementation detail. It is “unexported” every time a command is parsed.) export
doesn’t show _
because it isn’t a variable which is marked as exported; you can however see it in the output of set
.
In the first example, the new Bash shell parses and executes the commands in its startup files, so when running explore | grep '-='
, _
has already been overwritten and marked as not exported.
In the dash
example, it doesn't seem to execute any start-up file, so you’re seeing the variable as an environment variable that was set by Bash before running dash
.
When you run a docker container, you're running isolated from the calling environment. Variables aren't directly inherited.
We can see a "clean" environment, which we can see by creating a totally minimal container.
e.g. a go
program:
package main
import "os"
import "fmt"
func main() {
for _, e := range os.Environ() {
fmt.Println(e)
}
}
We can build this into a tiny container:
FROM scratch
ADD tester /
ENTRYPOINT ["/tester"]
And if we run this:
$ docker run tst
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=e598bf727a26
HOME=/root
These are variables created by the docker engine at runtime.
So when you run .. /bin/sh
you're running a non-login shell that just inherits the environment docker creates. Since it's not a login shell, /etc/profile
isn't run. /bin/sh
will, itself, create some default variables if they do not exist.
$ docker run -it alpine
/ # env
HOSTNAME=51667ed06110
SHLVL=1
HOME=/root
TERM=xterm
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
PWD=/
So there are a number of ways you can do this. Off the top of my head, here's two ideas:
You can pass environment variables on the docker command line with -e
.
$ docker run -e myvariable=testing -it alpine /bin/sh
/ # echo $myvariable
testing
You could build your own image based on alpine with ENV
commands:
$ cat Dockerfile
FROM alpine
ENV myothervar=anothertest
$ docker build -t myalpine .
...
$ docker run -it myalpine
/ # echo $myothervar
anothertest
Basically, what you're seeing is the docker run time providing some variables, /bin/sh
providing other variables, and isolation from the calling environment.
Best Answer
For POSIX-compatible shells (including Bash), the standard says:
And about
export
:So from the shell's point of view, there are only variables. Some of them may have come from the environment when the shell was started, and some of them may be exported to the environment of the processes the shell starts.
(The "environment" is really just a bunch of strings passed to the process when it starts. When the process is running, it can do whatever it likes with that, use it, ignore it, overwrite it. And what a process passes on when starting other processes can be yet another thing, though of course it's usual to just pass all of the environment variables along again.)
If you were using some non-POSIX shell, such as
csh
, things might be different: