24

I don't understand why bash completion is loaded so slow on my MacBook Pro.

I did the following in my ~/.bash_profile:

echo "Loading BashCompletion..."
if [ -f /opt/local/etc/bash_completion ]; then
    . /opt/local/etc/bash_completion
fi
echo "BashCompletion loaded."

the execution time for bash_completion typically is > 2 seconds.

I find that really annoying when I am working on the terminal which requires me to constantly open new tabs.

Is there a way I can cache this or something?

(Note I am using iTerm2 and this is equally slow on the original terminal in Mac as well).

9
  • That should not be happening. Am I correct you use MacPort's bash completion?
    – slhck
    Jul 15, 2011 at 20:27
  • What does that file you load look like?
    – Daniel Beck
    Jul 15, 2011 at 20:48
  • @slhck: Yes I am indeed using macport's bash completion
    – xjq233p_1
    Jul 15, 2011 at 21:17
  • @Daniel: Everything is fine except for this. I profiled almost every line.
    – xjq233p_1
    Jul 15, 2011 at 21:17
  • 5
    I experience the same slowness and I'm using Homebrew.
    – bric3
    Jan 30, 2012 at 11:52

6 Answers 6

22

For anyone coming to the conclusion that startup times for new shells on MacOS are too slow for them, this is the solution.

I just discovered that there are in fact two packages that can be installed via brew. I've been installing the bash-completion package for years, and never bothered to question it, even though in that time I've gone from Bash 3, to 4, to now 5. From time to time, though, I'd revisit the problem, often stumbling upon this very StackOverflow discussion.

There's another package, bash-completion@2!

What's the difference? bash-completion is for Bash version 3.2. bash-completion@2 is for Bash version 4.1+, and 5.

In removing the old bash-completion package, and installing bash-completion@2, my shell startup times have dropped from 605ms to 244ms. That's a huge speed improvement.

I suspect many of us are making this same mistake, as the brew info stats show that the former has tonnes of installs, while the latter has so few:

enter image description here

It should be noted that the current chosen answer mentions commenting out some lines, which provides only a slight improvement to startup times (if using the old bash-completion package, which many probably are), but has no impact whatsoever on the new bash-completion@2 package: this new package is fast no matter what. That means no hacks required.

TL;DR:

brew uninstall bash-completion && brew install bash-completion@2

Remember to update the source path to the completion file in your .bashrc or .bash_profile file.

Sources:


As a topic somewhat related, I use the rclone utility a lot, so it's installed. It also happens to have the largest completion file I've ever seen. Removing it reduces my shell startup times to ~120ms, which is very fast.


Edit:

For anyone who wants the technical details that explain this problem, I've written about it at length on the Homebrew forums. To summarize, the reason that bash-completion@2 is so much faster is because it was written so that it no longer eagerly loads all the completion files; instead it loads a completion file on-demand, or as the author describes it, it loads them in a non-eager manner.

4
  • I think the default Bash version on macOS is still v3.2 - I don't think it ships with Bash v4.2. Do you have a reference where it says that macOS ships with Bash v4.2+?
    – nwinkler
    Apr 10, 2019 at 6:37
  • 1
    @nwinkler You would be correct. I'm puzzled why I happened to mention that, because MacOS still ships with version 3.2.57(1)-release (x86_64-apple-darwin18). Thanks for pointing that out; I've remove the line from my post. Apr 10, 2019 at 17:07
  • 1
    Baffling, I know... Thanks for updating your answer!
    – nwinkler
    Apr 10, 2019 at 17:18
  • Thanks! In my current version, the script has another name (with .sh extension). See brew info: ` [[ -r "/usr/local/etc/profile.d/bash_completion.sh" ]] && . "/usr/local/etc/profile.d/bash_completion.sh"`
    – anumi
    Dec 1, 2023 at 16:44
10

Short version: Removing a single line from /usr/local/etc/bash_completion reduced the time to open a new tab from ten seconds to a quarter of a second. Read on for details.

I'm using bash-completion from homebrew and encountered the same problem. It was taking over ten seconds to load the bash completion scripts each time I opened a terminal.

Most of that time, it seems is taken up by a single line in the have() function: a call to type to determine if a command-line program is installed.

With the default have() function and all of the provided bash completion scripts in place, it would take 10.561s to load the scripts (reported by prefixing time to the . /opt/local/etc/bash_completion line in my .bash_profile file.

After commenting out the PATH=$PATH:/sbin:/usr/sbin:/usr/local/sbin type $1 &>/dev/null && line of my /usr/local/etc/bash_completion script (leaving the have=yes line, opening a new terminal takes only 0.258s. This time could be reduced further by removing unnecessary completion scripts (symlinks) from the /usr/local/etc/bash_completion.d directory.

I don't know why the call to type is taking so long. I'm investigating that next.

One potential downside to this approach is that it will cause bash completion functions to be loaded into memory even though you have no use for them. The have() function checks to see if a command or application is installed. If it's not, the completion script generally decides not to bother loading itself because it will be of no use.

At the moment, I'm happy with the tradeoff but I will continue to explore the type problem as I get time. I'll update my answer if I find a better solution.

2
  • For me, commenting this line reduces 50ms time, from 230ms to 180ms. Of course I never had it so bad in the first place. 👍 Dec 28, 2017 at 2:44
  • This only shaved off about 60ms, so I've not kept the workaround. I don't have ten second wait times, but about 2s, which is mildly irksome. Feb 7, 2018 at 0:03
4

With the idea that godbyk answer gave me, I found that my PATH variable had a few directories that didn't have any binaries or didn't exists, removing them sped it up significantly. In other words, this is the PATH I had in my bashrc:

PATH="$GOPATH/bin:/some/directory/not/existing:/some/empty/directory:/some/directory/without/binaries:$PATH"

And then I changed it to:

PATH="$GOPATH/bin:$PATH"

This was because have function in that bash completion, looked for each command, and I had too much of useless directories that was going to be visited for each of those binaries, removing them sped it up.

1
  • I also managed to change the load time from around 5 seconds to less than 1 second by removing paths that don't exists from my PATH environment variable.
    – juriejan
    Jan 17, 2019 at 13:35
0

I had the same issue. A few simple debugging tricks got me to the root cause.

First, enable DEBUG mode so you can see what's happening:

export BASH_COMPLETION_DEBUG=true

This enables verbose printing to the console, so you can see the last command. You can now execute the script in the background and you'll see what's happening

. /opt/local/etc/bash_completion &

Take not of the PID, which you can then trace with ps or pstree:

pstree -p <the PID>:

| |     \-+= 82095 mfellows -bash
| |       \-+- 82103 mfellows -bash
| |         |-+- 82104 mfellows cargo --list
| |         | \--- 82106 mfellows rustc -vV --cap-lints allow

As you can see, it started some rust related commands, which were taking ages.

Temporarily removing /opt/boxen/homebrew/etc/bash_completion.d/cargo resolved my symptoms.

-1

If you're running MacPorts >= 2.1.2 and Mountain Lion it seems your bash_profile is wrong. Follow the instructions on How to get git-completion.bash to work on Mac OS X?. I assume that could speed up the auto-completion.

Another solution would be to try installing auto-complete via Fink or Homebrew. If that doesn´t work, you could try another shell altogether. I've found that Fish shell is outstanding when it comes to auto-completion (out of the box). Though version 2 is still in beta I would highly recommend it.

-1

I'm going to guess that your bash is too old. I'm running stock bash that came with Mountain Lion and here's what I see:

$ port info bash-completion
bash-completion @2.0, Revision 1 (sysutils)

Description:          Programmable completion library for bash. This port
                      **requires bash >=4.1** and is meant to be used together with
                      the bash port.
Homepage:             http://bash-completion.alioth.debian.org/

Runtime Dependencies: bash
Conflicts with:       bash-completion-devel
Platforms:            darwin
License:              GPL-2+
Maintainers:          [email protected]

$ bash --version
GNU bash, version **3.2.48(1)-release (x86_64-apple-darwin12)**
Copyright (C) 2007 Free Software Foundation, Inc.
2
  • I don't see to have this port command. :( How do I find out which git tab completion software is running on my mac. Aug 14, 2014 at 16:13
  • @DeanHiller This answer is referring to the Macports package manager, which provides the port command. Macports's bash completion app will be newer than the one provided with OS X.
    – Matt S
    Aug 25, 2014 at 16:12

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .