14

I'm working on a Bash library and want to ensure I'm supporting as many environments as possible - including old installations of Bash. My development environment is Bash 4.3, but some of my users may well be running much older versions and presently I have no way to confirm or deny that my library will work for them. In particular I'd like to be compatible with OSX (which still ships with Bash 3.2, AFAIK).

I know Bash can run in POSIX-compliant mode; is there a similar setting to disable modern functionality? Or a way to run Bash in some sort of compatibility mode? I'm looking for any technique short of actually finding and booting up old operating systems and testing my library there.

Update

For example, I've avoided using associative arrays since they were introduced in Bash 4, but it's hard to be sure without testing that I'm not accidentally using some other Bash 4+ feature.

dimo414
  • 47,227
  • 18
  • 148
  • 244
  • 1
    http://travis-ci.org/ lets you run your tests in a matrix of different environments without installing them locally. As a first baseline, you could set up different versions of Debian, which pull in different versions of Bash. Down the line, you could compile every single point release and add it to the matrix if you really want complete coverage. – tripleee Jan 15 '16 at 06:23
  • I hardly think a hypothetical "disable new functionality" option to Bash would actually reintroduce the bugs which were fixed between releases. – tripleee Jan 15 '16 at 06:24
  • That's a great suggestion; I'm unfortunately not at the point of running automated tests yet - I'm trying to manually verify behavior. As for reintroducing bugs, who knows? IE's [compatibility mode](https://en.wikipedia.org/wiki/Compatibility_mode) certainly reintroduces bugs. While I'd be surprised if Bash had a similar feature, that's what I'm currently envisioning, so I'm open to alternatives. – dimo414 Jan 15 '16 at 06:27
  • 1
    I would get a down-version source for Bash (the 3.25 version, for example), and configure it to be installed somewhere out of the way (`/usr/local/bin`, for example), and then compile and install it — as `/usr/local/bin/bash-3.25` for example). Then you can test with `bash-3.25` instead of just running `bash`. Your bigger problem migrating from Linux to Mac is likely to be the non-GNU utilities. If you use GNU extension options (compared with POSIX, in particular — or just those not supported by Mac), then you will have problems even if Bash understands what you said perfectly. Beware `sed`. – Jonathan Leffler Jan 19 '16 at 19:55
  • @JonathanLeffler good point about the non-GNU utilities. I *have* stipulated that the GNU utilities are a prerequisite, so that's not a concern for my specific case. – dimo414 Jan 19 '16 at 21:39

4 Answers4

16

Finally coming back to this question, it's pretty easy to just compile (without installing) the bash version(s) you're interested in. Here's how I'm testing Bash 3.2.57:

$ mkdir ~/bash
$ cd ~/bash
$ wget http://ftp.gnu.org/gnu/bash/bash-3.2.57.tar.gz
$ tar xvzf bash-3.2.57.tar.gz
$ cd bash-3.2.57
$ ./configure
$ make
# if `make` fails due to yacc, run `sudo apt-get install byacc`
# No need to run `make install`
$ ./bash -version
GNU bash, version 3.2.57(1)-release (armv7l-unknown-linux-gnu)
Copyright (C) 2007 Free Software Foundation, Inc.

Now you have a bash 3.2.57 binary you can run, without actually "installing" it or modifying your normal environment.

To run a shell script against this version:

$ ./bash your_script.sh

To enter a clean interactive prompt:

$ env -i PATH="$PWD:$PATH" ./bash --noprofile --norc
bash-3.2$ bash -version
GNU bash, version 3.2.57(1)-release (armv7l-unknown-linux-gnu)
Copyright (C) 2007 Free Software Foundation, Inc.
bash-3.2$ 

Using env -i rather than just calling ./bash directly leaves you with a mostly-empty environment (run env from inside the shell to see what's still set). Updating the PATH allows calls to bash (e.g. bash -version) to invoke the local bash shell, not the system-wide installation (but note this pulls in your whole PATH). Adding --noprofile --norc avoids loading your .bashrc and associated scripts.

If you don't want to pick up any PATH modifications, just execute export PATH="$PWD:$PATH" once inside the subshell instead of as part of the env command.


I have a Docker image (repo) using these installation steps, if that's helpful for folks to reference. I wouldn't necessarily suggest using this image directly, but you're welcome to copy from the Dockerfile/install script. MIT licensed.

dimo414
  • 47,227
  • 18
  • 148
  • 244
5

Although it's nice to know that it's possible to compile arbitrary versions of bash locally (as discussed in my other answer), these days there's a much simpler option - the official Docker bash images.

To test a script against multiple bash versions is often as simple as:

for v in 3 4 5; do # or whatever versions you're interested in
  docker run -v "$PWD:/mnt" "bash:$v" \
    bash /mnt/your_script.sh
done
dimo414
  • 47,227
  • 18
  • 148
  • 244
4

Have a look at shenv: https://github.com/shenv/shenv. Just like rbenv, pyenv, goenv and others, but for shells, it lets you install different versions of Bash among others (zsh, fish, yash, etc.).

(Disclaimer: I'm the one who forked pyenv into shenv!)

pawamoy
  • 3,382
  • 1
  • 26
  • 44
-1

You can use Bash own facilities to emulate older version of Bash. Look up "compat" options on shopt.

Mind you, while it does change behavior as described under each compatNN entry in the man page, it does not remove features that are otherwise present on the current version. For example, this doesn't cause any errors:

shopt -s compat31
shopt -s globstar

Even though globstar was only introduced on Bash 4.0.

Daniel C. Sobral
  • 295,120
  • 86
  • 501
  • 681
  • However as that pages notes these `compatXX` settings only change very specific behaviors; it doesn't cause Bash to switch fully over to to the given version's syntax and semantics. `compat31` only changes certain behaviors of `=~` and `[[`. Notably, you can still use `declare -A` with in a shell with `compat31` on. – dimo414 Jun 14 '18 at 23:41
  • @dimo414 And you felt the need to repeat what I said why? – Daniel C. Sobral Jun 16 '18 at 19:41
  • "You can use Bash own facilities to emulate older version of Bash [via `compat`]" is not correct. The `compat` flags are *not* an emulation feature, but a backwards-compatibility feature. Using the `compat` flags does not accomplish the goals stated in the question. – dimo414 Jun 17 '18 at 03:42
  • @dimo414 To be pedantic, to emulate means "reproduce the function or action of (a different computer, software system, etc.)", which the compat flags do. What you said in your comment is what I said in the second paragraph. Maybe you got enraged and jumped straight to comment without actually reading the full answer? Regardless, all you said is in my answer. – Daniel C. Sobral Jun 18 '18 at 00:51
  • There's no rage here, your post simply doesn't answer the question. The intent is to verify that a Bash script will work with older versions of Bash. The `compat` flags do not accomplish that goal. – dimo414 Jun 18 '18 at 15:29