19

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 '11 at 20:27
  • What does that file you load look like? – Daniel Beck Jul 15 '11 at 20:48
  • @slhck: Yes I am indeed using macport's bash completion – disappearedng Jul 15 '11 at 21:17
  • @Daniel: Everything is fine except for this. I profiled almost every line. – disappearedng Jul 15 '11 at 21:17
  • 5
    I experience the same slowness and I'm using Homebrew. – Brice Jan 30 '12 at 11:52
13

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.

3
  • 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 '19 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. – danemacmillan Apr 10 '19 at 17:07
  • 1
    Baffling, I know... Thanks for updating your answer! – nwinkler Apr 10 '19 at 17:18
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. 👍 – Edward Anderson Dec 28 '17 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. – danemacmillan Feb 7 '18 at 0:03
3

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 '19 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:          raimue@macports.org

$ 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. – Dean Hiller Aug 14 '14 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 '14 at 16:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.