In this case, you need to upgrade Bash on macOS. In this case, you need to upgrade Bash on macOS

One thing many macOS users don’t know is that they are using a completely outdated version of the Bash shell. Therefore, it is highly recommended to use a newer version of Bash on macOS, as it has many new features available. This article describes how to do this.

Default version of Bash on macOS

To see if the version of Bash included in macOS is out of date, run the following command:

$bash --version GNU bash, Release (x86_64-apple-Darwin19.0.0) Copyright (C) 2017 Free Software Foundation, Inc. Licence GPL + : version 3 of the GNU GPL license or updated version < http://gnu.org/licenses/gpl.html > this software is free software, you can free to change and redistribute. Where permitted by law, the software does not provide any warranty.Copy the code

As you can see, this is GNU Bash version 3.2, with a date of 2007! This version of Bash is included in all macOS releases, even the latest.

The reason Apple included such an older version of Bash in its operating system had to do with licensing. Starting with version 4.0 (a successor to 3.2), Bash uses the GNU General Public License v3 (GPLv3), which Apple does not (want to) support. You can find some discussion of this here and here. Version 3.2 of GNU Bash is the latest version accepted by Apple with GPLv2, so stick with it.

This means that the entire world (Linux, for example) will be using the new version of Bash, while macOS users will only be able to use the older version from ten years ago. At the time of this writing, the latest version of GNU Bash is 5.0 (see here), released in January 2019. In this article, I’ve given instructions for upgrading your system’s default Shell to the latest version of Bash.

Why upgrade?

But if Bash 3.2 works, why bother upgrading? Personally, the main reason is programming completion. A new Bash feature is support for auto-completion of specific commands. You might use autocomplete for commands, filenames, and variables by typing and then tabbing to autocomplete the current word (or tabbing twice to get a list of all possible completers, if there are more than one). This is auto-completion for Bash.

However, programming completion is much more than that, as it allows context-dependent, command-specific completion. For example, imagine typing CMD -[TAB][TAB] and seeing a list of all the options that apply to that command. Or type CMD host rm [TAB][TAB] and see a list of all “hosts” specified in a configuration file. Programmatic completion can do this.

The programmable completion logic (by the creator of the command) is defined in the completion specification, usually in the form of a completion script. These completion scripts must be provided in the Shell to enable the completion of the command.

The problem is that Bash’s programmable completion capabilities have been extended since version 3.2, and most of the completion scripts use these new features. This means that these completion scripts don’t work on Bash 3.2, which means that if you continue with the default macOS shell, you’ll miss a lot of command completion.

By upgrading to newer versions of Bash, you can use these completion scripts, which can be very useful. I wrote an entire article called Bash Programming Completion on macOS, which covers everything you need to know to take full advantage of programmable completion on macOS after upgrading to newer versions of Bash.

How to upgrade?

To upgrade the default Shell on macOS to the latest version of Bash, you have to do three things:

  1. Install the latest version of Bash
  2. Use the new Bash Whitelist as the login shell
  3. Set the new Bash to the default shell

Each step is very easy, as described below.

** Note: ** The following instructions do not change the old version of Bash, but instead install the new version and set it to the default version. Both versions will coexist on your system, but you can ignore the old version here.

The installation

I recommend using Homebrew to install the latest version of Bash:

The brew bash installed

That’s it!

To verify the installation, you can check that there are now two versions of Bash on your system:

$ which -a bash
/usr/local/bin/bash
/bin/bash
Copy the code

The first is the new version, and the second is the old version:

$ /usr/local/bin/bash --version
GNU 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 --version
GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin18)
Copyright (C) 2007 Free Software Foundation, Inc.
Copy the code

In the PATH variable, the default PATH of the new /usr/local/bin directory will precede the older /bin directory, so just type bash to use the new version

$bash --version GNU bash, version 5.0.0(1)-release (x86_64-apple-darwin18.2.0)Copy the code

So far, so good. You have now set this version as the default.

White list

UNIX includes a security feature that limits shell programs that can be used as the Login shell to a list of “trusted” shell programs. The Shell in the/etc/shells (https://bash.cyberciti.biz/guide//etc/shells) in the file list.

Since you are using the newly set Bash Shell as the default Shell, it must be able to act as the login Shell. This means that you have to add it to the /etc/shells file. You can edit this file as root:

$ sudo vim /etc/shells
Copy the code

And add the /usr/local/bin/bash Shell to its contents so that the file looks like this:

/bin/bash
/bin/csh
/bin/ksh
/bin/sh
/bin/tcsh
/bin/zsh
/usr/local/bin/bash
Copy the code

That’s what this step requires!

Setting the default Shell

At this point, if a new terminal window opens, Bash 3.2 will still be used. This is because /bin/bash is still set as the default Shell program. To change it to the new shell, execute the following command:

$ chsh -s /usr/local/bin/bash
Copy the code

And so, for now, the default Shell program for the current user is set to the new version of Bash. If you close and reopen the terminal window, you should now be using the new version. You can verify as follows:

Echo $BASH_VERSION 5.0.0 (1) - releaseCopy the code

The CHSH command changes the default Shell program only for the user executing the command (the current user). If you want to change the default Shell, other users will be logged in to the other user’s identity (for example, using [su] (Unix) (https://en.wikipedia.org/wiki/Su_)) to repeat this command. On top of that, you may want to change root’s default Shell program. You can do the following:

$ sudo chsh -s /usr/local/bin/bash
Copy the code

This way, if you sudo su to open the Shell as root, it will use the new version of Bash.

Important notes

Usage in scripts

As mentioned earlier, instead of changing the default version of Bash, you installed the new version and set it to the default version. Both versions of Bash exist side by side on your system:

  • /bin/bash: the old version
  • /usr/local/bin/bashNew version:

In shell scripts, you will often have a shebang line, as shown in the following script:

#! /bin/bash echo $BASH_VERSIONCopy the code

Be sure to note that the shebang line explicitly references an older version of Bash (because it specifies /bin/bash). This means that if you run this script, it will be interpreted by an older version of Bash (you can see it in the output of the script, similar to 3.2.57(1)-release).

In most cases, this may not be a problem. However, if you want the script to be interpreted explicitly by newer versions of Bash, you can change the shebang line to look like this:

#! /usr/local/bin/bash echo $BASH_VERSIONCopy the code

The output will now be 5.0.0(1)-release. Note, however, that this solution is not portable, which means it may not run on other systems. This is because other systems may not have shell /usr/local/bin/bash in them (whereas /bin/bash is almost standard).

To combine the best of both worlds, you can use the following shebang rows:

$ sudo rm /bin/bash
$ sudo ln -s /usr/local/bin/bash /bin/bash
Copy the code

For shebang rows, this is the recommended format. It works by checking PATH and using the first bash executable encountered as the interpreter for the script. If the new version’s directory is before the old version directory PATH (the default) in, the new version will be used, and the output of the script will be similar to 5.0.0(1)-release.

Why can’t you use symbolic links?

In order not to handle both versions of Bash, can you remove the old version and put the new version in the old location? For example, by creating a symbolic link to the new version in /bin/bash, as shown below?

$ sudo rm /bin/bash
$ sudo ln -s /usr/local/bin/bash /bin/bash
Copy the code

That way, even scripts with scripts #! /bin/bash is also explained by new versions of bash, so why not?

You can do this, but you must avoid a macOS security feature called System Integrity Protection (SIP) ** ** (Wikipedia). This feature even disables write access to certain directories by the root user (which is why it is also called “rootless”). These directories are listed here and include /bin. This means that even as root, you cannot execute the above commands because you are not allowed to delete anything from it or create any files in /bin.

The solution is to disable SIP, make changes in /bin, and then enable SIP again. You can enable and disable SIP by following the instructions here. It requires you to boot your computer into recovery mode and then use the csrutil disable and csrutil enable commands. Whether you want to completely replace the old version of Bash in this particularly complicated way, or accept both versions of Bash at the same time, is up to you.

reference

  • apple.stackexchange.com/a/292760