1

I've got an almost 100 line .bash_profile which does things like check the git branch and colour certain text. This makes things like even starting a new line significantly slower compared to the instant speed of having an empty profile.

Is there any way around this or is it just because of the extra functionality that the profile has which is adding overhead? Why is it being so slow?

After testing it on another machine (on a Fedora distro, was using Mac), it doesn't have this issue at all. The Fedora machine is instant as expected, while the Mac has a noticeable delay when starting a new line, cd'ing into something etc.

Here is the profile: https://pastebin.com/UKWPPqKf

# .bashrc

# Source global definitions
if [ -f /etc/bashrc ]; then
    . /etc/bashrc
fi

# User specific environment
if ! [[ "$PATH" =~ "$HOME/.local/bin:$HOME/bin:" ]]
then
    PATH="$HOME/.local/bin:$HOME/bin:$PATH"
fi
export PATH

# Uncomment the following line if you don't like systemctl's auto-paging feature:
# export SYSTEMD_PAGER=

# User specific aliases and functions

#THIS MUST BE AT THE END OF THE FILE FOR SDKMAN TO WORK!!!
export SDKMAN_DIR="/home/USRNAME/.sdkman"
[[ -s "/home/USRNAME/.sdkman/bin/sdkman-init.sh" ]] && source "/home/USRNAME/.sdkman/bin/sdkman-init.sh"

#Aliases
if [ -f ~/.bash_aliases ]; then
    source ~/.bash_aliases
fi

# get current branch in git repo
function parse_git_branch() {
    BRANCH=`git branch 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/\1/'`
    if [ ! "${BRANCH}" == "" ]
    then
        STAT=`parse_git_dirty`
        echo "[${BRANCH}${STAT}]"
    else
        echo ""
    fi
}
# get current branch in git repo
function parse_git_branch() {
    BRANCH=`git branch 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/\1/'`
    if [ ! "${BRANCH}" == "" ]
    then
        STAT=`parse_git_dirty`
        echo "[${BRANCH}${STAT}]"
    else
        echo ""
    fi
}

# get current status of git repo
function parse_git_dirty {
    status=`git status 2>&1 | tee`
    dirty=`echo -n "${status}" 2> /dev/null | grep "modified:" &> /dev/null; echo "$?"`
    untracked=`echo -n "${status}" 2> /dev/null | grep "Untracked files" &> /dev/null; echo "$?"`
    ahead=`echo -n "${status}" 2> /dev/null | grep "Your branch is ahead of" &> /dev/null; echo "$?"`
    newfile=`echo -n "${status}" 2> /dev/null | grep "new file:" &> /dev/null; echo "$?"`
    renamed=`echo -n "${status}" 2> /dev/null | grep "renamed:" &> /dev/null; echo "$?"`
    deleted=`echo -n "${status}" 2> /dev/null | grep "deleted:" &> /dev/null; echo "$?"`
    bits=''
    if [ "${renamed}" == "0" ]; then
        bits=">${bits}"
    fi
    if [ "${ahead}" == "0" ]; then
        bits="*${bits}"
    fi
    if [ "${newfile}" == "0" ]; then
        bits="+${bits}"
    fi
    if [ "${untracked}" == "0" ]; then
        bits="?${bits}"
    fi
    if [ "${deleted}" == "0" ]; then
        bits="x${bits}"
    fi
    if [ "${dirty}" == "0" ]; then
        bits="!${bits}"
    fi
    if [ ! "${bits}" == "" ]; then
        echo " ${bits}"
    else
        echo ""
    fi
}

export PS1="\[$(tput bold)\]\u@\h:\[$(tput sgr0)\]\[\033[38;5;39m\]\w\[$(tput sgr0)\]\[$(tput sgr0)\]\[\033[38;5;15m\] \[$(tput sgr0)\]\[\033[38;5;11m\]\[$(tput bold)\]\`parse_git_branch\`\[\e[m\] \[$(tput sgr0)\]"
  • Is there any way to put some of these commands in a script file and run them separately instead of every time you open a shell window? – jmh Aug 20 at 20:24
  • I suppose it would have to know that it's in a folder with a Git repository, but I wouldn't know how to do that. Any ideas? – Forumpy Aug 20 at 20:26
  • you could put echo statements in your .bash_profile and see if one or a few commands are taking a long time to run. Maybe place an echo statement every 10 lines and see if a particular set takes a long time. – jmh Aug 20 at 20:29
  • What do you mean by starting a new line? Just pressing Return or open a new tab? – nohillside Aug 20 at 20:32
  • 1
    please post your profile. – historystamp Aug 20 at 21:07
1

If you type set -x into the terminal, it will print every command it executes from that point on, so to find the culprit, type set -x and hit return, then - paying attention to the lines scrolling by on the screen - watch for whatever command takes a long time to run.

The number of + characters at the beginning of each line tell you how deeply nested the command is. If you scroll up a bit, you can find the "parent" command.

The commands you see when you hit return are all triggered by the last line of your bashrc, where you set the contents of the variable PS1. Every time you hit return, the contents of that variable are executed and the result is shown as the "prompt" (the bit before the cursor, e.g. something like user@box ~ $).

To get out of this mode, you can type set +x or just close that terminal window/tab (see What does set -x do?).


PS: I would hazard a guess that your git status commands are slowing down the prompt. If you install both git and bash-completion from Homebrew, you can use __git_ps1 to do this and get rid of all the git related functions from your bashrc. As an example, this is what it looks like on my machine:

if [[ -f /usr/local/etc/bash_completion ]]; then
    . /usr/local/etc/bash_completion
fi

GIT_PS1_SHOWDIRTYSTATE=1
GIT_PS1_SHOWUPSTREAM="auto"
GIT_PS1_SHOWUNTRACKEDFILES=1

PS1='\w \$ '
if type __git_ps1 &>/dev/null; then
    PS1='\w$(__git_ps1 | sed -e "s/=)$/)/") \$ '
fi

You must log in to answer this question.

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