What is the bashrc equivalent in alpine linux ash?
Asked Answered
M

3

11

The title pretty much says it all (but maybe there's something more sinister in play.) I'm aware of /etc/profile.d/*.sh scripts that run when you login but that doesn't seem to work as expected when you simply start a shell. Here's an example with docker:

Create a small script :

$ echo "echo hello world" > startup.sh

Run the alpine shell with the script mounted like so:

$ docker run -it --rm -v `pwd`/startup.sh:/etc/profile.d/statup.sh alpine sh

All you'll get is a shell prompt:

/ #

It does print "hello world" when you su - from here though:

/ # su -
hello world
2bf679a5677d:~# 

But a simple sh to start a shell will not do:

/ # sh
/ #

So, what's the issue here? Is it an obscure Alpine Linux thing? Is it an obscure Alpine Linux Docker image thing?

Maine answered 4/10, 2022 at 22:50 Comment(2)
Not obscure, not specific to alpine. Try running sh yourscript.sh or sh -c 'echo "running" and neither will print "hello world" (unless yourscript.sh contains instructions to do so).Dialectician
By default, Alpine Linux uses ash as shell. You have therefore to define the additional file to be sourced on startup by setting ENV. This is explained here in the section Invocation.Taler
D
6

For interactive shells

As man ash tells you in the Invocation section, the environment variable ENV can be used to specify a file to source during shell startup.

In your Dockerfile:

ENV ENV=/home/youruser/.rc

...and then your shell will execute the contents of /home/youruser/.rc during startup.


In bash, BASH_ENV (used when not in POSIX mode) runs even for noninteractive shells; when bash is in POSIX mode, it runs ENV for interactive shells only (which is the only circumstance in which the POSIX specification requires ENV to be honored).

Dialectician answered 4/10, 2022 at 23:28 Comment(3)
This applies even to noninteractive, non-login shells. This is incorrect: source1, source2.Molybdous
@ynn, read linux.die.net/man/1/ash (labeled as the manual for 1999 BSD ash) -- you've convinced me that ash breaks POSIX behavior, but its manual is explicit on this point. (By contrast, dash and busybox ash both honor the letter of the spec and ignore ENV in this case).Dialectician
What I want to say is the documentation is incorrect in the first place. How to reproduce: (1) Write this Dockerfile: pastebin.com/drbkBQ3X (2) Execute docker build . -t test_alpine to build the image. (3) Execute docker run -it --rm test_alpine to start an interactive session. It prints hello, meaning ENV is respected. (4) Execute docker run -it --rm test_alpine sh -c 'echo world' to start a non-interactive session. This time, no hello is printed, meaning ENV is ignored at all.Molybdous
G
6

Dockerfile RUN commands and the main container CMD don't run any shell dotfiles, ever, on any shell, on any base Linux distribution. This is true even if your base image includes GNU bash or you've manually reset the SHELL to run bash instead of standard sh.

The GNU Bash Reference Manual probably has the best description of when dotfiles get read. There are three cases: a shell can be a non-interactive shell; it can be an interactive shell but not a login shell; or it can be a login shell specifically. In a Docker container, the shells you encounter usually aren't interactive shells, and if they are, they aren't login shells.

The important corollary to this is that writing shell dotfiles in Dockerfiles at all is usually incorrect. Most paths to running commands in containers won't read them. If you need to set an environment variable, use an ENV directive instead.

Some examples:

CMD ["/usr/local/bin/my_program"]

This exec form CMD doesn't run a shell at all. There is no sh process, and nothing will ever read any shell dotfiles.

CMD my_program
# CMD ["/bin/sh", "-c", "my_program"]

This shell form CMD is automatically wrapped in /bin/sh -c '...', but the shell that produces is a non-interactive shell, and doesn't read dotfiles.

docker run --rm my_image my_program

Again, this doesn't run a shell.

docker run --rm my_image sh -c 'my_program'

This explicitly supplies a shell as part of the command string, but it's not an interactive shell.

docker run --rm -it my_image sh

The main container command is a shell, and you haven't given it a command, and you have provided a stdin; so in this case it's an interactive shell. If it were bash it would read .bashrc but not any other dotfiles.

docker run --rm -it my_image bash --login

The interactive shell is only a login shell if you explicitly request it. In pretty much only this invocation, the full set of shell dotfiles will get read.

/etc/profile.d on its own isn't a standard shell feature. It's a convenience provided by some distributions, but not something specifically mentioned in the bash manual. Compare for example

docker run --rm ubuntu cat /etc/profile
docker run --rm bash cat /etc/profile
docker run --rm alpine cat /etc/profile
docker run --rm busybox cat /etc/profile

Notice that, in the first three cases, the /etc/profile files are different, but all of them contain logic to read an /etc/profile.d directory. Your invocation isn't running a login shell so that file isn't getting read.

In the normal case, the thing your container is running won't be a shell. Prebuilt images like docker run postgres or docker run nginx are typical examples: there is some specific piece of software packaged in the image, and running the container runs exactly that single piece of software. Starting an Nginx HTTP server doesn't require a shell and doesn't read shell dotfiles.

Grosvenor answered 4/10, 2022 at 23:18 Comment(1)
David, thank you so much for the in depth explanation! I genuinely got smarter reading your post. After giving a second look at Charles' answer I decided to switch the "correct" checkmark over to him. I feel his answer might be more relevant to the general "bash vs alpine" title of this question as oppose to my specific use case.Maine
D
6

For interactive shells

As man ash tells you in the Invocation section, the environment variable ENV can be used to specify a file to source during shell startup.

In your Dockerfile:

ENV ENV=/home/youruser/.rc

...and then your shell will execute the contents of /home/youruser/.rc during startup.


In bash, BASH_ENV (used when not in POSIX mode) runs even for noninteractive shells; when bash is in POSIX mode, it runs ENV for interactive shells only (which is the only circumstance in which the POSIX specification requires ENV to be honored).

Dialectician answered 4/10, 2022 at 23:28 Comment(3)
This applies even to noninteractive, non-login shells. This is incorrect: source1, source2.Molybdous
@ynn, read linux.die.net/man/1/ash (labeled as the manual for 1999 BSD ash) -- you've convinced me that ash breaks POSIX behavior, but its manual is explicit on this point. (By contrast, dash and busybox ash both honor the letter of the spec and ignore ENV in this case).Dialectician
What I want to say is the documentation is incorrect in the first place. How to reproduce: (1) Write this Dockerfile: pastebin.com/drbkBQ3X (2) Execute docker build . -t test_alpine to build the image. (3) Execute docker run -it --rm test_alpine to start an interactive session. It prints hello, meaning ENV is respected. (4) Execute docker run -it --rm test_alpine sh -c 'echo world' to start a non-interactive session. This time, no hello is printed, meaning ENV is ignored at all.Molybdous
P
3

For anyone coming here using alpine linux, or specially through WSL, just add .profile file into the root directory.

apk update
apk add nano
cd ~
nano .profile
---
alias test='I work'
source ~/.profile
test
>>> I work
Parfitt answered 30/8, 2023 at 14:10 Comment(1)
That source makes the answer meaningless: you could put content in a file with any name and source would activate its contents. .profile is only run automatically in login shells, whereas .bashrc is run automatically in interactive shells; they have different use cases.Dialectician

© 2022 - 2024 — McMap. All rights reserved.