Determine previous color used in PS1 - bash

Right now my PS1 looks like this
I want that "▸" background color to change depending on the color of the last section. So, if I wasn't in a git repo, it should be blue but when I'm in a git repo, yellow.
Here is what my PS1 looks like in my .bash_profile
# git info on prompt
function __git_info() {
local -r SYMBOL_GIT_BRANCH="⑂";
local -r SYMBOL_GIT_MODIFIED="*";
local -r SYMBOL_GIT_PUSH="↑";
local -r SYMBOL_GIT_PULL="↓";
hash git 2>/dev/null || return 0; # git not found
# current branch reference
local ref=$(git symbolic-ref --short HEAD 2>/dev/null);
# if it's not a normal branch name, get tag name or short unique hash
[[ -z "$ref" ]] && ref=$(git describe --tags --always 2>/dev/null);
[[ -n "$ref" ]] || return 0; #not a git repo
local following; # ahead/behind count
local modified; # whether something has been modified locally
local extras; # additional info
local status; # status of the repo
local untracked; # whether or not there are untracked files
local staged; # whether or not there are staged files
status=$(git status 2>&1 | tee);
untracked=$(printf "%s" "$status" 2> /dev/null | grep -m 1 "Untracked files" &> /dev/null; printf "%s" "$?");
staged=$(printf "%s" "$status" 2> /dev/null | grep -m 1 "Changes to be committed" &> /dev/null; printf "%s" "$?");
[[ "${untracked}" == "0" ]] && extras+="?";
[[ "${staged}" == "0" ]] && extras+="+";
# scan first two lines of output from `git status`
while IFS= read -r line; do
if [[ $line =~ ^## ]]; then #header line
[[ $line =~ ahead\ ([0-9]+) ]] && following+="$SYMBOL_GIT_PUSH${BASH_REMATCH[1]}"
[[ $line =~ behind\ ([0-9]+) ]] && following+="$SYMBOL_GIT_PULL${BASH_REMATCH[1]}"
else #branch is modified if output contains more lines after the header
modified=" $SYMBOL_GIT_MODIFIED";
break;
fi;
done < <(git status --porcelain --branch 2>/dev/null);
# print the git branch segment without a trailing newline
printf "%s" " [$SYMBOL_GIT_BRANCH$following $ref$modified$extras] ";
}
## Prompt customizations ##
function __host() {
printf '\[\e[30;102m\] \h \[\e[0m\]';
}
function __dir() {
printf '\[\e[1;97;44m\] \w \[\e[0m\]';
}
function __git_status() {
printf "\[\e[30;43m\]\$(__git_info)\[\e[0m\]";
}
function __arrow() {
printf '\[\e[1;97;44m\] ▸ \[\e[0m\]';
}
export PS1="$(__host)$(__dir)$(__git_status)$(__arrow) "
Anyone got any ideas how this can be accomplished? I tried setting global vars, but the PS1 is using subshells so that won't work.

Well, your __git_info function returns a status, so why not use it? (Make sure you have it return NON-zero when you ARE is a git repo.) Don't reset the colors in the function, but allow them to remain as they are and reset them after the arrow:
function __dir() {
printf '\[\e[1;97;44m\] \w ';
}
function __git_status() {
local info=$(__git_info)
[ $? -ne 0 ] && printf "\[\e[30;43m\]$info";
}
function __arrow() {
printf ' ▸ \[\e[0m\]';
}
export PS1="$(__host)$(__dir)$(__git_status)$(__arrow) "

Related

Why of this strange behavior from my custom bash prompt?

This is my script for my custom prompt (.bashrc):
my_prompt () {
if [[ $? -eq 0 ]]; then # set an error string for the prompt, if applicable
ERRPROMPT="✔"
else
ERRPROMPT="✘"
fi
if [[ "\$(type -t __git_ps1)" ]]; then # if we're in a Git repo, show current branch
BRANCH="\$(__git_ps1 '[ %s ] ')"
fi
if [[ -n "$SSH_CLIENT" ]]; then
REMOTE="💻${SSH_CLIENT%% *} "
fi
if [[ $EUID -eq 0 ]]; then
COLUSER=9 # root
else
COLUSER=112 # human
fi
local BKG1="\[\033[48;5;${COLUSER}m\]"
local FBKG1="\[\033[38;5;${COLUSER}m\]"
local BKG2="\[\033[48;5;11m\]"
local FBKG2="\[\033[38;5;11m\]"
local BOLD="\[$(tput bold)\]"
local FBLACK="\[\033[38;5;0m\]"
local FLIGHTBLUE="\[\033[38;5;159m\]" #195
local FLIGHTGRAY="\[\033[38;5;244m\]"
local ICON1="${BOLD}${FLIGHTBLUE}"
local ICON2="${BOLD}${FBLACK}${BKG2}"
local RESET="\[$(tput sgr0)\]" # "\[\033[0;39m\]"
local TEXT1="${RESET}${FLIGHTGRAY}"
local TEXT2="${RESET}${FBLACK}${BKG2}"
if [[ -n "$DESKTOP_SESSION" || -n "$REMOTE" ]] ; then
echo "\n${BKG1}${FBLACK}${ERRPROMPT} 🙽\u#\h${RESET}${FBKG1}▎${REMOTE}${ICON1}🗁${TEXT1}\w ${ICON1}🕔${TEXT1}\A\n${ICON2}🗁${TEXT2}🙼\W${RESET}${FBKG2}▎${BOLD}⌘${RESET} "
else
echo '[\u#\h \W]\$ '
fi
}
export PS1=`my_prompt`
The desired output is like this:
The problem occurs when navigating history (keys up/down) and sometimes pressing key Home, my cursor is positioned in a place that does not correspond or the text displayed overwrites prompt or the cursor overwrites text displayed. See this picture:
So, where is the problem? and Why of it? What's the solution? Thanks in advance.

why my function does not execute in PS1?

get_git_branch(){
local branch__=
git branch &> /dev/null
if [ $? -eq 0 ]; then
branch__=`git branch --no-color | sed -ne 's/^\* \(.*\)$/\1/1p' | tr a-z A-Z`
else
branch__="NORMAL"
fi
echo -n $branch__
}
exit_status(){
local smile__=
if [ $? -eq 0 ]; then
smile__='(*´▽`*)'
else
smile__='(╥﹏╥)'
fi
echo -n $smile__
}
export PS1='[\w]\d\t\$\n\u->(`get_git_branch`)`exit_status`:'
This is PS1 setting in my bashrc,I want to check git branch and exit status in my terminal, get_git_branch works every time PS1 refreshed, but exit_status not, whey exit_status not executed?
It absolutely is executed. However, $? is changed by other code that's run before it -- like get_git_branch.
The best practice here is not to embed code where you want detailed flow control in PS1, but rather to use PROMPT_COMMAND.
get_git_branch(){
local branch
if branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null); then
printf '%s\n' "${branch^^}" # if on bash 3.2, you may need to use tr instead
else
echo "NORMAL"
fi
}
exit_status(){
if (( ${1:-$?} == 0 )); then
printf '%s' '(*´▽`*)'
else
printf '%s' '(╥﹏╥)'
fi
}
build_prompt() {
last_exit_status_=$?
PS1='[\w]\d\t\$\n\u->($(get_git_branch))$(exit_status "$last_exit_status_"):'
}
PROMPT_COMMAND=build_prompt

Stop git commit in pre-commit hook with exit

I'm trying to stop a git commit from continuing with a pre-commit hook. From reading the manual, it should stop when you return an exit code other than 0. I'm testing to see if the csscomb command returns an error, and if it does then break the loop and exit, but the git commit still continues on to entering a message via git commit (-a).
I've forked and modified the script below from https://github.com/filtercake/git-pre-commit-csscomb
#!/bin/bash
# pre-commit hook to comb staged .sass and .scss files
# save as .git/hooks/pre-commit
# make executable: chmod +x .git/hooks/pre-commit
# sources:
# http://www.hagenburger.net/BLOG/Using-Git-Commit-Hooks-to-Autocompile-Sass-to-CSS.html
# http://stackoverflow.com/questions/8470755/git-pre-commit-hook-add-file-into-index/8471009#8471009
# http://stackoverflow.com/questions/592620/how-to-check-if-a-program-exists-from-a-bash-script/677212#677212
# https://gist.github.com/openam/8406343
# check if sass/scss files are staged for commit
# diff-lines from http://stackoverflow.com/a/12179492/3019532
# takes the line
function diff-lines() {
local path=
local line=
while read; do
esc=$'\033'
if [[ $REPLY =~ ---\ (a/)?.* ]]; then
continue
elif [[ $REPLY =~ \+\+\+\ (b/)?([^[:blank:]$esc]+).* ]]; then
path=${BASH_REMATCH[2]}
elif [[ $REPLY =~ ##\ -[0-9]+(,[0-9]+)?\ \+([0-9]+)(,[0-9]+)?\ ##.* ]]; then
line=${BASH_REMATCH[2]}
elif [[ $REPLY =~ ^($esc\[[0-9;]+m)*([\ +-]) ]]; then
echo "Line $line:$REPLY"
if [[ ${BASH_REMATCH[2]} != - ]]; then
((line++))
fi
fi
done
}
if ! git diff --quiet --cached -- **/*.{sass,scss}; then
echo ""
echo "--> you checked in sass/scss files. lets comb them..."
echo ""
# check if csscomb is installed
if hash csscomb 2>/dev/null; then
echo -e "\033[1;32m--> found csscomb\033[0m"
# TODO: check for csscomb update once a week
# check if configfile exists
if [ ! -f ./.csscomb.json ]; then
echo "--> no config file, using defaults"
else
echo -e "\033[1;32m--> found .csscomb.json\033[0m"
fi
echo ""
# Necessary check for initial commit
against="4b825dc642cb6eb9a060e54bf8d69288fbee4904"
git rev-parse --verify HEAD >/dev/null 2>&1 && against="HEAD"
EXITCODE=0
# (A)dded (C)opied or (M)odified
# encapsulate the loop in {} http://stackoverflow.com/a/13665483/3019532
git diff-index --cached --full-index --diff-filter=ACM $against | \
{
while read -r line; do
FILE_PATH="$(echo ${line} |cut -d' ' -f6-)"
EXTENSION="${FILE_PATH##*.}"
# EXTENSION=${EXTENSION,,} # Convert to lowercase
REGEX=""
# Select discouraged words based on extension
if [ "${EXTENSION}" = "sass" ] || [ "${EXTENSION}" = "scss" ]; then
echo "--> staged sass/scss file: " $FILE_PATH
echo "--> combing..."
if csscomb $FILE_PATH; then
echo "--> adding combed file"
git add $FILE_PATH
echo "--> done"
else
echo "Check your CSS file for combing errors or alternatively add '-n' to your git commit to bypass this hook"
break
exit 1 # Should stop the git commit from continuing
fi
fi
done
}
else
echo -e "\033[0;31m--> Oh noes, CSS Comb is not installed. Do:"
echo "--> npm install csscomb -g"
echo -e "\033[0m"
echo "--> (you need to change and stage a sass/scss file again to see it work on the next commit...)"
echo ""
fi
fi
# Necessary check for initial commit
against="4b825dc642cb6eb9a060e54bf8d69288fbee4904"
git rev-parse --verify HEAD >/dev/null 2>&1 && against="HEAD"
EXITCODE=0
exit 0
git diff-index --cached --full-index --diff-filter=ACM $against | \
{
while read -r line; do
Here's your trouble. multi-command pipe stages are run in subshells, and the exit from that loop exits its subshell. Subshells have their own environments, too, so you can't pass variable settings along from them either.
while read -r; do
if ! good $REPLY; then exit 1; fi
done <<EOD
$(git diff-index --cached --full-index --diff-filter=ACM $against)
EOD

Mac OSX | Bash .bash_profile not updating PS1

I have a small script named .bash_prompt which is called by source ~/.bash_prompt in ~/.bash_profile.
The script sets my PS1 to display some useful information about the current git repo.
Unfortunately the git-part is only being executed when spawning a new terminal, so the branch is only displayed when I call the script manually after changing to a git repo.
How can I make my bash prompt update everytime I execute a command?
function git_branch() {
local GITDIR=$(git rev-parse --show-toplevel 2>&1)
if [[ "$GITDIR" != '/Users/\u' ]]
then
local BRANCH=`git branch 2> /dev/null | sed -n '/^\*/s/^\* //p'`
if [ -n "$BRANCH" ]; then
echo -e "$BRANCH"
fi
else
echo ""
fi
}
function git_prompt() {
local prompt_unpushed_symbol="△"
local prompt_unpulled_symbol="▽"
local prompt_dirty_symbol="*"
local prompt_synced_symbol="✓"
local local_branch=$(git_branch)
local remote_branch="origin/$local_branch"
local first_log="$(git log $local_branch $remote_branch -1 2> /dev/null)"
local STATUS=`git status 2>&1`
if [[ "$STATUS" == *'Not a git repository'* ]]; then
echo ""
elif [[ "$STATUS" != *'working directory clean'* ]]; then
echo "[$local_branch $prompt_dirty_symbol]"
elif [[ "$STATUS" == *'Your branch is ahead'* ]]; then
echo "[$local_branch $prompt_unpushed_symbol]"
elif [[ -n "$first_log" ]]; then
echo "[$local_branch $prompt_unpulled_symbol]"
else
echo "[$local_branch $prompt_synced_symbol]"
fi
}
function colorPrompt {
local c_brace="\[\033[m\]"
local c_git="\[\033[31m\]"
local user_host="\[\033[36m\]\u\[\033[m\]#\[\033[32m\]\h"
local location="\[\033[33;1m\]\w"
local tail="\n\$ "
export PS1="[$user_host $location$c_brace]$c_git$(git_prompt)$c_brace$tail"
}
colorPrompt
The value of the PROMPT_COMMAND shell variable is executed prior to displaying the prompt; one of the main uses of this feature is to set the value of PS1. In your case, all you need to do is add
PROMPT_COMMAND=color_prompt
to your .bash_profile after sourcing .bash_prompt.

Why does my bash prompt sometimes get overwritten?

Here's the relevant parts of my bashrc:
function find_git_branch {
local dir=. head
until [ "$dir" -ef / ]; do
if [ -f "$dir/.git/HEAD" ]; then
head=$(< "$dir/.git/HEAD")
if [[ $head == ref:\ refs/heads/* ]]; then
git_branch=" (${head#*/*/})"
elif [[ $head != '' ]]; then
git_branch=' (detached)'
else
git_branch=' (unknown)'
fi
return
fi
dir="../$dir"
done
git_branch=''
}
function shortpath {
# How many characters of the $PWD should be kept
local pwd_length=40
local lpwd="${PWD/#$HOME/~}"
if [ $(echo -n $lpwd | wc -c | tr -d " ") -gt $pwd_length ]
then newPWD="...$(echo -n $lpwd | sed -e "s/.*\(.\{$pwd_length\}\)/\1/")"
else newPWD="$(echo -n $lpwd)"
fi
echo $newPWD
}
PROMPT_COMMAND="find_git_branch; $PROMPT_COMMAND"
# PS1 prompt color vars
CYAN="\e[36m"
RED="\e[31m"
GREEN="\e[32m"
DEFAULT="\e[0m"
TIME="[\t]"
DIRNAME="\w"
export PS1="\u#\h:\[$CYAN\]\$(shortpath)\[$GREEN\]\[\$git_branch\]\[$DEFAULT\] \$ "
It works well, but sometimes as I type or hit the up arrow for previous commands, part of the prompt gets overwritten in the terminal. Why does this happen?
Looks like you're including your $git_branch part in a non-printing-chars block (\[...\]).

Resources