Contribute
Register

[GUIDE] Install latest version of bash on Catalina: No Homebrew, MacPorts, or other package manager required

Status
Not open for further replies.
Joined
Jul 17, 2019
Messages
123
Motherboard
Asus TUF Gaming Z590-PLUS WIFI
CPU
i9-10900K
Graphics
RX 6800 XT
Mac
  1. MacBook Pro
  2. Mac mini
Mobile Phone
  1. iOS
st,small,507x507-pad,600x600,f8f8f8.jpg

With the release of Catalina, Apple has officially changed the default shell to zsh. This will have many advantages, as the included version of bash in macOS is very old; however, zsh is technically not fully POSIX compliant, and there exist a number of great scripts that simply do not work correctly on zsh. Fortunately, getting bash back is very easy. One of the requirements I had when doing this myself was to have no dependence on brew, or MacPorts, as these leave a noticeable footprint. In the case of MacPorts, it will build everything against its on dependencies; often resulting in the duplication, or even triplication of common runtimes, libraries, etc. As such, both methods detailed in this post will not require the installation of a third party package manager.

Method #1 - reverting to classic bash

The version of bash that we've come to expect in macOS hasn't gone anywhere; however, it is technically deprecated. If you're looking to revert to something that you're used to from Mojave, High Sierra, or other previous versions of macOS, this is the choice for you. To do this, follow the steps listed below

  1. Open System Preferences and select Users & Groups
  2. Click the padlock icon to unlock these preferences, as this is required to change the relevant settings.
  3. Right click on your users account in the left column and click Advanced Options...
  4. You should see a series of options; some of which include setting the location of your home directory, changing your name, etc. Among these options look for "Login shell", and from the dropdown menu, select /bin/bash.
  5. Apply the changes, logout, and log back in for said changes to take affect.
Alternatively, this change can be made much quicker by firing up the terminal, and entering the following command
Bash:
$ chsh -s /bin/bash

After entering your password, the default shell for your account will be changed. Of course, you'll need to logout and back in for this change to take affect.

Please be aware that support for anything deprecated is non existent. If there is a bug in bash, it is unlikely that Apple will ever fix it. The same is also true for security vulnerabilities; so, use this version at your own risk!


Method #2: building the latest version of bash from source

This is the preferred way to do this, as the latest version of bash will bring all the improvements that have been made since 2007, as well as bug fixes, and most importantly, security patches. This post assumes little to no experience with building from source, and as such, I will include some extra detail. Before we can begin, make sure that you have installed the Xcode command line tools. To do this, open terminal, and issue the command
Bash:
$ xcode-select --install

11/23/2020 IMPORTANT: BUILD ERROS ON 10.15.x - PLEASE READ!
So, for some reason, bash 5 will not compile against the newer xCode 12 command line tools, and as such we need to download the last set of tools for xCode 11. You can download the tools, directly from Apple, simply by clicking here. You must sign in with your AppleID and accept the developer terms of service before you can access any of the provided resources. This does not obligate you to pay for a developer account; instead, it serves as a launching point for getting started with development within the Apple ecosystem. I'm not sure when this issue will be resolved; however, please keep in mind that bash 5.1 will likely be released very soon, as we have already seen 3 different release candidates.

This does not require the entire Xcode app and takes up much less space. Depending on your hardware, and internet connection, installation time will vary, but in my experience, it doesn't take too long. Next, download the latest version of the bash source. In this post, I will be linking GNU's ftp server; however, you may see better results using a mirror that is geographically closer to your location. This post will focus on bash 5.0 since it is currently the latest release. As of bash 5.0.18, autoconf is also a requirement and bash will fail to build if it is not present; so, we will need to download the latest version of autoconf as well. While you can download the archives from a web browser, you should become familiar with using the command line, as from this point onward, we'll be working in the terminal almost exclusively. Use the following commands to download each archive. While lumping them into a single command is definitely possible, I am going to keep them separated for the sake of consistency.
Bash:
$ curl https://ftp.gnu.org/gnu/bash/bash-5.0.tar.gz -O
Bash:
$ curl https://ftp.gnu.org/gnu/autoconf/autoconf-2.69.tar.xz -O
This will place the file in the current working directory. Typically, the default directory you're working in when launching a new terminal window is your home folder. Do not get in the habit of working in your home folder; instead, create subdirectories to keep things organized. For the sake of brevity, we'll just stick with the home directory; for better, or for worse. With the archives downloaded, use the tar command to extract each archive.
Bash:
$ tar xvf autoconf-2.69.tar.xz && cd autoconf-2.69
Bash:
$ tar xjf bash-5.0.tar.gz && cd bash-5.0
Both of these commands will navigate to the respective directories that were extracted from each archive; so, we're going to start with autoconf first, as it is a dependency for bash, and therefor, do not run the command for bash until we are done dealing with autoconf. This is the norm for source code compilation; you must build any dependencies first, and work your way up to the main program you're trying to install. Some programs have webs of dependencies that must be built first ... sounds annoying? Well, there is a special name for this type of dependency tracking; however, since expletives are not permitted, I'm just going to have to leave it at that. Now that we are in the main autoconf source directory, we need to invoke the configure script in order to set the appropriate parameters for compilation. While it is perfectly acceptable to run it as is, we're going to make some changes to keep things a little bit cleaner. For specific reasons, some may not wish to have autoconf in /usr/local and therefor, we are going to place the compiled binaries in a directory within our user's home folder. To do this, issue the following command.
Bash:
$ ./configure --prefix=/Users/$USER/opt
As we can see, this will set autoconf to use ~/opt as its prefix instead of the default /usr/local found within the configure script. Once the configure script has finished doing its thing, it's time to compile the binaries. This can be done with the following command.
Bash:
$ make -j 6
The number after -j represents the number of CPU cores you wish to dedicate to compilation. Because I'm using an i5 9600k, which lacks hyper threading, the maximum value for me is 6; however, if your CPU supports hyper threading, you should enter the number of threads you wish to use. For example, those using something like an i9 9900k can go as high as 16. Assuming you didn't get any errors, you can install your newly compiled software by issuing the following command.
Bash:
$ make install
Notice how this command is not proceeded with sudo; that's because we are installing these binaries to a directory which your user has write permissions, and it is there for not needed. Now we need to move out of the autoconf source directory to get back to where we downloaded the archive to, as to begin working with the bash source code.
Bash:
$ cd ..
Now we can execute the above tar command for the bash archive. Like the tar command used for autoconf, the associated bash command will navigate you to the newly extracted directory. While we now have the extracted source code, we're still not ready to compile. Since its release, there have been 18 patches for bash 5.0 as of writing this which should be applied. To do this, we need to create a patches folder inside the main bash-5.0 folder.
Bash:
$ mkdir patches && cd patches
While downloading 18 patches separately sounds like a chore, that's because it is; however, a simple adaptation of the previously used curl command will allow us to download all 18 at once.
Bash:
$ curl 'https://ftp.gnu.org/gnu/bash/bash-5.0-patches/bash50-[001-0118]' -O
With the patches downloaded, it's time to go back up a directory and begin applying them.
Bash:
$ cd ..
$ patch -p0 -i patches/bash50-001
$ patch -p0 -i patches/bash50-002
$ patch -p0 -i patches/bash50-003

...

While there exists a command that will attempt to apply all patches at once, it may require some additional steps, and will not be covered here. Now that all patches have been applied, it's time to compile the source into binaries. We first need to make sure that the folder where the compiled autoconf binaries were installed is present in our $PATH. Think of this variable as a list of places in which bash, or other common shells will attempt to look for programs, libraries, headers, dependencies, etc. Like how we read English, this variable is parsed from left to right; that is, directories listed on the left will be checked first, and those closer to the right will be checked last. To add our previously built autoconf, we can do so with the following command.
Bash:
$ PATH=/Users/$USER/opt/bin:$PATH
This will add the newly defined directory to the front of our $PATH variable without altering the rest. This method is temporary and your $PATH variable will return to its previous state upon closing that particular instance of terminal. Now with that out of the way, we can now begin the process for compiling bash. Like before, we need to execute the configure script.
Bash:
$ ./configure
$ make -j 6

Like before, make sure you specify the appropriate number of cores/threads for compilation. Of course, there is no rule that says you must use every core that you have. If there is other work which you need to keep working on, you of course may wish to specify fewer cores.

Assuming there are no errors, you should now have the bash binaries ready to be installed. Because we are cautious, we are going to check our compiled binaries for issues before installing.

Bash:
$ make check
$ sudo make install

NOTE: If you're fine with living life on the edge, you can skip make check, and go straight to install. Once finished, you should now have an up-to-date version of bash located in /usr/local/bin that should work as expected.

Code:
$ /usr/local/bin/bash --version
#expected output
GNU bash, version 5.0.16(1)-release (x86_64-apple-darwin19.4.0)
Copyright (C) 2019 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

There's still one final step that needs to be done before we can use the previously mentioned "chsh" command to set this new version of bash as our default login shell. In the /etc directory, you'll find a file called shells that contains the locations of all valid unix shells on your system. The installation script does not automatically add your new version of bash to this file, and therefor, we need to add it ourselves.

Bash:
sudo nano /etc/shells

The file should look something like the following; you'll notice that I went ahead, and added /usr/local/bin to the end of the file.

Code:
# List of acceptable shells for chpass(1).
# Ftpd will not allow users to connect who are not using
# one of these shells.

/bin/bash
/bin/csh
/bin/dash
/bin/ksh
/bin/sh
/bin/tcsh
/bin/zsh
/usr/local/bin/bash

Once you have added /usr/local/bin/bash to the end of the file, use ctrl+x to exit nano (don't forget to save). For this change to take affect, you may have to reboot your system before attempting to change your default login shell. There's a good chance that /usr/local/bin/bash will not show up as an option following the graphical system preferences steps listed above; so, just use the terminal again to change your default login shell.

Bash:
$ chsh -s /usr/local/bin/bash

Notice how this command is not, and should not be executed via sudo; doing so will have an impact on the root user. Aside from that heads up, that's it; you should now be using your newly compiled bash by default! Please keep in mind that these instructions are subject to change with future releases of bash; so, if you're reading this in the future, consult more current documentation. I will attempt to keep this thread up-to-date as time goes on.
 

Attachments

  • full_colored_light.jpg
    full_colored_light.jpg
    85.9 KB · Views: 83
Last edited:
pkg.png

Creating a distributable package installer
You've got your shiny new version of bash and perhaps you want to use it on other Macs; however, you quickly notice a problem. There are some systems in which you really do not wish to have developer tools installed; be it for security reasons, such as preventing one less attack vector on a server, or perhaps this system will be used by users who really don't have much business having access to compilers in the first place (e.g patrons at a local library). Whatever the reason is, having said tools on these other systems is a complete non starter. Fortunately, we can very easily roll our newly compiled bash binaries into a .pkg installer; so, it can easily be deployed to other Macs.

Ideally, you'd want a copy of your bash source folder in which everything has been compiled, but "make install" has not yet been run. This is not a strict requirement, but I'm just really picky about this kind of stuff. In the parent directory of where you have your bash source folder, you want to go ahead, and create another folder. The name is completely arbitrary, but for this example, we'll use pkgpayload.
Bash:
$ mkdir pkgpayload
Copy your bash source folder into this new directory. I strongly recommend this kind of separation, as it'll make keeping track of things a bit easier later on down the line when you update bash. I renamed my bash folder to bash-5.0-18 in order to reflect the version of bash that I'm working with.
Bash:
$ cp -r bash-5.0-18 pkgpayload
$ cd bash-5.0-18/
Once within the root of your bash source directory, make a new directory called "payload".
Bash:
$ mkdir payload
If your folder already contains binaries, you can skip rerunning the make command, but if it needs to be reran, it shouldn't take very long. Assuming you already have compiled binaries, simply run the following modification of make install.
Bash:
$ make install DESTDIR=/Users/$USER//pkgpayload/bash-5.0-18/payload
Upon completion, you'll notice a replication of the /usr/local directory structure within the payload folder.
macOS comes with pkgbuild as part of the command line tools/xCode and invoking it is fairly simple. Once we have our binaries installed in the staging directory, all we need to do is run pkgbuild.
Bash:
$ pkgbuild --root payload --install-location / --identifier org.gnu.bash --version 5.0.18 bash-5.0.18.pkg
If we look at the command, the structure is fairly straight forward.
  • --root: defines the path of the items you wish to include in the pkg installer. Please note that the "payload" folder itself will not be included, and only its contents will.​
  • --install-location: This is set to "/" since the /usr/local/bin structure is already defined, and therefor, we don't need to specify anything more specific.​
  • --identifier: identifies the name of the developer, as well as the name of the software itself. This is technically arbitrary, but please make this human readable (especially if you intend to share your pkg).​
  • --version: defines version number​
The final portion simply specifies the file name of your installer. As we can see from the above command, I went with bash-5.0.18.pkg for the sake of simplicity. Because this is a very basic setup, the installer will not add /usr/local/bin bash at the end of the file /etc/shells and it therefor must be done manually.

...and that's it? Yes, at this point, you should have a fully working bash 5 installer that can be used on just about any Mac. Please keep in mind that these steps in both this, as well as the previous post, were performed on macOS Catalina 10.15.7. I have successfully performed these steps on macOS 11 given the relevant SDKs; however, your milage may vary. If the steps remain mostly the same on Big Sur with the release of bash 5.1, I'll simply append the title of this thread to indicate that these instructions are valid for Big Sur. Of course, if you have any questions, suggestions, criticism, and everything in between, please feel free to drop a comment, as user feedback is greatly appreciated!

-Aldaro
 
Status
Not open for further replies.
Back
Top