bash on macOS is still bash v3:
$ bash --version GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin18) Copyright (C) 2007 Free Software Foundation, Inc.
Just recently, bash v5 was released. The discrepancy comes from the fact that
bash has been licensed as GPL v3 since version 4. Apple does not include GPL v3 licensed tools with macOS.
However, nothing is keeping you from downloading and installing the latest
New features include, among many other things, associated arrays (i.e. dictionaries) and better auto-completion setup.
While you would think this is a common desire, most pages I have found will simply point to Homebrew to download and install a newer bash version.
The main challenge with using
brew is that it does not work on the scale that MacAdmins require.
brew is designed for single user installation, where the user has administrator privileges.
brew’s workflows do not scale to large deployments controlled with a management system.
Ideally, there would be package installer for the latest bash version. Unfortunately, the bash project does not provide one.
In this post, I will show how you can install the latest bash version without
brew and how to build an installer package for deployment.
This requires Xcode or the Developer Command Line Tools to be installed.
First, download the source for the latest bash version from this page. As of this writing the latest version is
bash-5.0 and the file you want is
bash-5.0.tar.gz. Once downloaded, you can expand the archive in Finder by double-clicking.
Open a Terminal window and change directory to the newly expanded
bash-5.0 directory. Then run the
configure script there.
$ cd ~/Downloads/bash-5.0 $ ./configure
The configure process will take a while, there will be plenty of messages showing progress.
configure process is complete. You can build
bash with the
This will build the
bash binary and the supporting files in the current directory. That’s not where we want it in the end, but it is probably a good idea see if the build process works. This will (again) take a while. There will be some odd looking warnings, but you can ignore those.
make succeeds, you can actually install
bash v5 with
$ sudo make install
This will build and install the
bash binary and supporting files in
sudo is required to modify
If you were just looking for a way to install
bash v5 without
brew, you are done!
There is more useful information in the rest of the post, though, so keep reading!
How the new and the old bash interact
By default, the bash v5 binary is called
bash and will be installed in
/usr/local/bin. The macOS default
/bin where the default bash v3 binary, also called
bash, is located.
This means, that when a user types
bash in to a shell, the version in
/usr/local/bin will be preferred over the pre-installed bash v3.
You can test this behavior in Terminal. Since the default shell has not yet been changed from
/bin/bash the Terminal still opens to bash v3. You can test this by showing the
BASH_VERSION environment variable:
$ echo $ BASH_VERSION 3.2.57(1)-release
But when you then run
bash it will invoke
/usr/local/bin/bash, so it will run the new bash v5. It will show this in the prompt, but you can also verify the
$ bash bash-5.0$ echo $ BASH_VERSION 5.0.0(2)-release
This might be the setup you want, when you want to use bash v5 always. It might lead to some unexpected behavior for some users, though.
One option to avoid this ambiguity is to rename the binary in
bash5. But then other tools such as
env (mentioned below) will not find the binary any more.
- Scripting OS X: Where PATHs come from
PATH in other contexts will likely not contain
/usr/local/bin and further confuse matters.
bash v5 and Scripting
bash, should have the full path to the binary in the shebang. This way, the script author can control whether a script is executed by the default bash v3 (
/bin/bash) or the newer bash v5 (
It is often recommended to use the
env command in the shebang:
env command will determine the path to the
bash binary in the current environment. (i.e. using the current
PATH) This is useful when the script has to run in various environments where the location of the bash binary is unknown, in other words across multiple Unix and Unix-like platforms. However, this renders the actual version of
bash that will interpret the script unpredictable.
For example, assume you have bash v5 installed in the default configuration (as
/usr/local/bin/bash. A script with the shebang
#!/usr/bin/env bash launched in the user environment (i.e. from Terminal) will use the newer
/usr/local/bin comes before
/bin in the search order.
When you launch the same script in a different context, e.g. as an installation script, an AppleScript, or a management system,
/usr/local/bin will likely not be part of the
PATH in that environment. Then the
env shebang will choose
/bin/bash (v3). The script will be interpreted and might behave differently.
Administrators prefer certainty in their managed environments. Administrators should know the location and versions of the binaries on their systems. For management scripts, you should avoid
env and use the proper full path to the desired interpreter binary.
The solutions to resolve the ambiguity are
- use the full path to the binary in the shebang
- manage and update the additional custom version of
bashwith a management system
- (optional) rename the newer
bash4(this also allows you to have
bashv5 available on the same system)
- Scripting OS X: On the Shebang
- Scripting OS X: Setting the PATH in Scripts
Changing a user’s default Shell to bash v5
Even though we have installed bash v5, the default shell of a new Terminal window will still use the built-in bash v3.
The path to the default shell is stored in the user record. You can directly change the
UserShell attribute with
dscl, in the ‘Advanced Options’ of the ‘Users & Groups’ preference pane, or in Directory Utility.
There is also a command to set the default shell:
$ chsh -s /usr/local/bin/bash Changing shell for armin. Password for armin: chsh: /usr/local/bin/bash: non-standard shell
chsh (change shell) command will check for allowed shells in the
/etc/shells file. You can easily append a line with
/usr/local/bin/bash to this file, and then
chsh will work fine.
$ chsh -s /usr/local/bin/bash Changing shell for armin. Password for armin:
Note: if you choose to rename the
bash binary, you have to use the changed name in
/etc/shells and with
Remember that just running
chsh will not change the shell in the current Terminal window. It is best to close the old Terminal window and open a new one to get the new shell.
Packaging bash v5 for mass deployment
While these steps to install and configure bash v5 on a single Mac are simple enough, they would not work well with a management system for hundreds or thousands of Macs. We want to wrap all the files that
make install creates into a package installer payload.
--help option of the
configure script yields this useful information:
make install' will install all the files in/usr/local/bin
etc. You can specify an installation prefix other than/usr/local
, for instance–prefix=$ HOME`.
When we run the configure script with the
--prefix option it creates a folder suitable as a payload for a package installer. We can then use
pkgbuild to build to create an installer pkg:
$ cd ~/Downloads/bash-5.0 $ mkdir payload $ ./configure --prefix=/Users/armin/Downloads/bash-5.0/payload $ make install $ pkgbuild --root payload --install-location /usr/local --identifier org.gnu.bash --version 5.0 bash-5.0.pkg pkgbuild: Inferring bundle components from contents of payload pkgbuild: Wrote package to bash-5.0.pkg
--prefix argument requires an absolute path.)
Automate the package creation
So, we have our workflow for building an installer package to distribute and configure bash v5:
- download the archive
- extract the archive
make installto create the files in a payload folder
- optional: rename the resulting
bash5to avoid conflicts
- add a
postinstallscript that adds
/etc/shellsif not yet present
- build the installer with
This sounds like a workflow ripe for automation. You can get the script from this repository.
You can pass a different (valid) bash version number as an argument to the script, e.g.
4.4.18. (I did not test anything significantly older.) The script does not autodetect the latest version and defaults to version
5.0 when no argument is given. When an update to bash v5 is published, you will have to modify the version line or run the script with an argument.
I have not (yet) figured out how to detect the latest version from the download web page. An
autopkg recipe will have to wait for that. (If someone else wants to tackle that, please do!)