A "POSIX Playground" Container for Shell Script Testing
Tags: bash, linux, docker
Published: 2021-02-23 11:36:27

After exploring [portable scripting in the previous article][article1], I built a [container available on Docker Hub][container], useful for testing and experimentation.

The container is an [Alpine Linux][alpine] container with the following tools:

- [posh] shell (this is the default)
- [dash] shell
- [rlwrap] to offer readline support for either of the above shells (`rlwrap posh` is the default command if none specified)
- [checkbashisms] for rooting out Bash-specific idiosyncrasies in scripts
- [shellcheck] for shell script linting

See the ["Writing Bash Scripts that are not only Bash"][article1] for explanation and usage of the above.

## Using the container

([Podman] also works fine in any of the examples below.)

First, pull the image:

```bash
docker pull docker.io/bowmanjd/posix-playground
```

Note that the default user is `shelly` and the home directory is `/home/shelly`

To launch [posh: Policy-compliant Ordinary SHell][posh] with readline support:

```bash
docker run -it bowmanjd/posix-playground
```

To instead launch [dash] shell with readline support:

```bash
docker run -it bowmanjd/posix-playground rlwrap dash
```

(Without `rlwrap`, line editing in either of these shells is impossible, making experimentation rather painful. Using `rlwrap` is recommended.)

Note that the container runs as an unprivileged user (as common sense dictates!) named `shelly` and the home directory is `/home/shelly`.

To make every file in the host's current working directory available from within the home directory inside the container, you might bind mount the current directory like this:

```bash
docker run -it -v "$(pwd):/home/shelly" bowmanjd/posix-playground
```

If using rootless [podman] (and likely SELinux), you will find it convenient to add the `:z` SELinux label to the bind mount to indicate a shared volume, allowing read-write acces, then use the `keep-id` user namespace in order to map the user id of `shelly` to your current user, as follows:

```bash
podman run -it --userns=keep-id -v "$(pwd):/home/shelly:z" bowmanjd/posix-playground
```

To check a `helloworld.sh` shell script in the current directory for "bashisms" (make sure the first line, the "[shebang]", reads `#!/bin/sh`):

```bash
cat helloworld.sh | docker run -i bowmanjd/posix-playground checkbashisms
```

To check a `helloworld.sh` shell script in the current directory for a variety of gotchas:

```bash
cat helloworld.sh | docker run -i bowmanjd/posix-playground shellcheck -
```

Be sure to include the `-` on the end: `shellcheck -` to let shellcheck know to expect stdin, as you are "piping" the script file contents into shellcheck as standard input.

To simply run a script using [posh]:

```bash
docker run -i -v "$(pwd):/home/shelly" bowmanjd/posix-playground posh myscript.sh
```

## Posh: an obscure shell with great usefulness

Of the tools included in the image, [posh] is the winner for me. It is not available on every distro, so having a container available is quite convenient.

Posh is also quite strict, and this is the point. It does not have any syntactic sugar to make it more Bash-like. I feel quite reassured when a script runs correctly in posh.

If you have other tools to recommend, or want to reflect on your experiences with this container image, feel free to contact me!

[alpine]: https://alpinelinux.org/
[article1]: https://www.bowmanjd.com/bash-not-bash-posix/
[ash]: https://en.wikipedia.org/wiki/Almquist_shell
[bash]: https://www.gnu.org/software/bash/
[checkbashisms]: https://manpages.debian.org/unstable/devscripts/checkbashisms.1.en.html
[container]: https://hub.docker.com/repository/docker/bowmanjd/posix-playground/
[cut]: https://pubs.opengroup.org/onlinepubs/9699919799/utilities/cut.html
[dash]: http://gondor.apana.org.au/~herbert/dash/
[devscripts]: https://github.com/Debian/devscripts
[podman]: https://podman.io/
[posh]: https://manpages.debian.org/unstable/posh/posh.1.en.html
[posix-shell]: https://pubs.opengroup.org/onlinepubs/9699919799/utilities/contents.html
[posix]: https://pubs.opengroup.org/onlinepubs/9699919799/
[rlwrap]: https://github.com/hanslub42/rlwrap
[shebang]: https://en.wikipedia.org/wiki/Shebang_(Unix)
[shellcheck]: https://www.shellcheck.net/