Upgrading Bash on macOS

The main reason, for me personally, is programmable completion.

This is the feature that allows you to type a command, filename, variable, or any other token token partially, and then hit Tab to auto-complete it (or hit Tab two times to display a list of all the possible matches).

Programmable completion does exist in Bash 3.

2, and you certainly use it a lot for auto-completing commands and filenames.

However, what many macOS users don’t know is that it also allows command-specific completions, which means that you can auto-complete command-specific sub-commands, options, and arguments.

Imagine, for example, that you type cmd -[tab][tab] and you instantly get a list of all applicable options.

Sounds useful, right?This is made possible by completion scripts that are provided by the individual commands.

The completion behaviours can be as sophisticated as the completion scripts that define them.

The problem is that the programmable completion features of Bash have been extended since version 3.

2, and most completion scripts use these new features.

This means that these completion scripts will not work with Bash 3.

2, and thus you cannot use the completion functionality provided by these commands with Bash 3.

2 (for example, the completion scripts of docker and kubectl just don’t work with Bash 3.

2).

By using a newer version of Bash, you become able to set up auto-completion for these commands, which can be extremely useful.

I will discuss in a follow-up article how to use command-specific auto-completion with Bash on macOS.

How To Upgrade?To upgrade the default shell of your macOS system to the latest version of Bash, you have to do three things:Install the latest version of Bash“Whitelist” new Bash as a login shellSet new Bash as the default shellEach step is extremely easy, as explained in the following.

Note: the following instructions don’t change the old version of Bash, but rather install a new version and set it as the default.

The two versions will exist side by side on your system, but you can just ignore the old version from there on.

InstallI recommend to use Homebrew to install the latest version of Bash:brew install bashThat’s it already!To verify the installation, you can check that you now have two versions of Bash on your system:$ which -a bash/usr/local/bin/bash/bin/bashThe first one is the new version, and the second one is the old version:$ /usr/local/bin/bash –versionGNU bash, version 5.

0.

0(1)-release (x86_64-apple-darwin18.

2.

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.

$ /bin/bash –versionGNU bash, version 3.

2.

57(1)-release (x86_64-apple-darwin18)Copyright (C) 2007 Free Software Foundation, Inc.

Since the directory of the new version (/usr/local/bin) comes by default before the directory of the old version (/bin) in the PATH variable, the version used when you just type bash is the new one:$ bash –versionGNU bash, version 5.

0.

0(1)-release (x86_64-apple-darwin18.

2.

0).

So far, so good.

Now you have make this version the default.

WhitelistUNIX includes a security feature that restricts the shells that can be used as login shells (i.

e.

the shell used after logging in to the system) to a list of “trusted” shells.

These shells are listed in the /etc/shells file.

Since you want to use the newly installed Bash shell as the default shell, it must be able to act as a login shell.

That means, you have to add it to the /etc/shells file.

You can edit this file as the root user:$ sudo vim /etc/shellsAnd add the /usr/local/bin/bash shell to its content, so that the file looks something like this:/bin/bash/bin/csh/bin/ksh/bin/sh/bin/tcsh/bin/zsh/usr/local/bin/bashThat’s it for this step!Set Default ShellAt this point, if you opened a new terminal window, you would still be using Bash 3.

2.

This is because /bin/bash is still set as the default shell.

To change this to your new shell, execute the following command:$ chsh -s /usr/local/bin/bashThat’s it!.The default shell for your current user is now set to the new version of Bash.

If you close and reopen the terminal window, you should now be using the new version already.

You can verify this as follows:$ echo $BASH_VERSION5.

0.

0(1)-releaseThe chsh command changes the default shell only for the user who executes the command.

If you want to change the default shell for other users too, you can repeat this command by assuming another user’s identity (e.

g.

with su).

Most importantly perhaps you might want to change the default shell for the root user, which you can do as follows:$ sudo chsh -s /usr/local/bin/bashIn this way, if you use sudo su to open a shell as the root user, it will also use the new Bash version.

Important NotesUsage in ScriptsAs mentioned, you didn’t change the default version of Bash, but rather installed a new version and set it as the default.

The two versions of Bash exist side by side on your system:/bin/bash: old version/usr/local/bin/bash: new versionIn shell scripts, you often have a shebang line like in the following script:#!/bin/bashecho $BASH_VERSIONIt is important to note that this shebang line explicitly refers to the old version of Bash (since it specifies /bin/bash).

This means that if you run this script, it will be interpreted by the old version of Bash (you can see it in the output of the script, which will be something like 3.

2.

57(1)-release).

For most cases, this is probably not a problem.

But if you wanted your script to be explicitly interpreted by the new version of Bash, you could change the shebang line like this:#!/usr/local/bin/bashecho $BASH_VERSIONNow the output will be something like 5.

0.

0(1)-release.

However, note that this solution is non-portable, which means, it is likely to not work on other systems.

This is because, other systems are likely to not have a shell located at /usr/local/bin/bash (whereas /bin/bash is almost a standard).

To combine the best of both worlds, you can use the following shebang line:#!/usr/bin/env bashecho $BASH_VERSIONThis is a recommended format for a shebang line.

It works by inspecting the PATH and using the first encountered bash executable as the interpreter for the script.

If the directory of the new version is located before the directory of the old version in the PATH (which is the default), then the new version will be used, and the output of the script will be something like 5.

0.

0(1)-release.

Why Not Symlink?Instead of dealing with both versions of Bash, couldn’t you just delete the old version and put the new version in its place?.For example, by creating a symlink in /bin/bash that points to the new version, like the following?$ sudo rm /bin/bash$ sudo ln /usr/local/bin/bash /bin/bashIn this way, even scripts with a #!/bin/bash would be interpreted by the new version of Bash, so why not doing this?You can do this, but you have to circumvent a macOS security feature called System Integrity Protection (SIP) (Wikipedia).

This feature forbids write access to certain directories for even the root user (that’s why it’s also called “rootless”).

These directories are listed here and include /bin.

That means that even as the root user, you cannot execute the above commands, because you are not allowed to delete anything from or create any files in /bin.

The way around this is to disable SIP, make the changes in /bin, and then enable SIP again.

Enabling and disabling SIP can be done according to the instructions here.

It requires you to boot your machine into recovery mode and then use the csrutil disable and csrutil enable commands.

It’s up to you if you want to go through this hassle to completely replace the old Bash version, or if you’re content with the two Bash versions living side by side.

Referenceshttps://apple.

stackexchange.

com/a/292760.

. More details

Leave a Reply