Skip to content

Instantly share code, notes, and snippets.

@Dinir
Last active May 4, 2026 12:40
Show Gist options
  • Select an option

  • Save Dinir/a344affcad551dcbb248 to your computer and use it in GitHub Desktop.

Select an option

Save Dinir/a344affcad551dcbb248 to your computer and use it in GitHub Desktop.
My Prompt
# ~/.bashrc snippet — bash port of Dinir's fish_prompt.fish
# Converted by Claude Opus 4.7.
# Drop this into your ~/.bashrc (or source it from there).
# ---- Color shortcuts (raw ANSI; wrapped with \[\] when used in PS1) ----
__c_blk=$'\e[30m'; __c_red=$'\e[31m'; __c_grn=$'\e[32m'
__c_yel=$'\e[33m'; __c_blu=$'\e[34m'; __c_mgt=$'\e[35m'
__c_cyn=$'\e[36m'; __c_lgry=$'\e[37m'
__c_gry=$'\e[1;30m'; __c_lred=$'\e[1;31m'; __c_lgrn=$'\e[1;32m'
__c_lyel=$'\e[1;33m';__c_lblu=$'\e[1;34m'; __c_lmgt=$'\e[1;35m'
__c_lcyn=$'\e[1;36m';__c_wht=$'\e[1;37m'
__c_nrm=$'\e[0m'
__c_redbg_blk=$'\e[41;30m'
# Default battery colors (overwritten by __prompt_battery)
__batColor=$__c_nrm
__batColorSub=$__c_nrm
# ---- Command duration tracking (fish's $CMD_DURATION equivalent) ----
__timer_start() {
# Only start the timer for the user's command, not for PROMPT_COMMAND itself.
[[ -n "$__timer_running" ]] && return
__timer_running=1
__timer_start_ms=$(( ${EPOCHREALTIME/./} / 1000 ))
}
__timer_stop() {
if [[ -n "$__timer_start_ms" ]]; then
local now_ms=$(( ${EPOCHREALTIME/./} / 1000 ))
CMD_DURATION=$(( now_ms - __timer_start_ms ))
else
CMD_DURATION=0
fi
unset __timer_start_ms __timer_running
}
trap '__timer_start' DEBUG
# ---- Section helpers ----
__prompt_section() {
# Prints a section followed by a comma separator (fish's prompt_section).
[[ -z "$1" ]] && return
printf '%s' "$1"
printf '%s, %s' "$__batColorSub" "$__c_nrm"
}
__color_section() {
# __color_section <color> <text>
printf '%s%s%s' "$1" "$2" "$__c_nrm"
}
# ---- Battery (Linux: acpi) ----
__prompt_battery() {
command -v acpi >/dev/null 2>&1 || return
local line status percent time
line=$(acpi 2>/dev/null | head -n1)
[[ -z "$line" ]] && return
status=$(grep -oE 'Full|Charging|Discharging|Not charging' <<< "$line" | head -n1)
percent=$(grep -oE '[0-9]+%' <<< "$line" | head -n1)
time=$(grep -oE '[0-9]+:[0-9]+' <<< "$line" | head -n1)
[[ -z "$time" ]] && time="===="
if [[ "$status" == "Charging" ]]; then
__batColor=$__c_lblu
__batColorSub=$__c_blu
__color_section "$__batColor" "$(printf '%3s' "${percent%\%}")"
__color_section "$__batColorSub" "%%"
elif [[ "$status" == "Full" ]]; then
__batColor=$__c_lcyn
__batColorSub=$__c_cyn
__color_section "$__batColor" "FULL"
else
if [[ "$time" == "====" ]]; then
__batColor=$__c_lgry
__batColorSub=$__c_lgry
__color_section "$__batColorSub" "===="
else
local h=${time%:*} m=${time#*:}
# Force base-10 interpretation; otherwise "08"/"09" trigger an octal error.
local timeMin=$(( 10#$h * 60 + 10#$m ))
if (( timeMin <= 10 )); then __batColor=$__c_lred; __batColorSub=$__c_red
elif (( timeMin <= 30 )); then __batColor=$__c_lyel; __batColorSub=$__c_yel
else __batColor=$__c_lgrn; __batColorSub=$__c_grn
fi
if (( timeMin < 300 )); then
__color_section "$__batColor" "${time%:*}"
__color_section "$__batColorSub" ":"
__color_section "$__batColor" "${time#*:}"
else
__color_section "$__batColorSub" "===="
fi
fi
fi
}
# ---- Load average ----
__prompt_load() {
local loadMin
loadMin=$(uptime | grep -oE '[0-9]+\.[0-9]+' | head -n1)
[[ -z "$loadMin" ]] && return
# Threshold: 0.60
local loadHundredths=${loadMin/./}
[[ -z "$loadHundredths" ]] && loadHundredths=0
if (( 10#$loadHundredths > 60 )); then
if [[ "$__batColor" == "$__c_lred" ]]; then
__color_section "$__c_redbg_blk" "$loadMin"
elif [[ "$__batColor" == "$__c_lyel" ]]; then
__color_section "$__c_red" "$loadMin"
else
__color_section "$__c_yel" "$loadMin"
fi
fi
}
# ---- Time ----
__prompt_time() {
printf '%s%s%s' "$__batColorSub" "$(date +%H:%M:%S)" "$__c_nrm"
}
# ---- Directory (fish's prompt_pwd: shorten parents to one char) ----
__prompt_pwd() {
local p="${PWD/#$HOME/\~}"
# Root or home: no shortening needed.
if [[ "$p" == "/" || "$p" == "~" ]]; then
printf '%s' "$p"
return
fi
# Detect and strip a leading ~ or / so we can split the rest cleanly.
local prefix=""
if [[ "${p:0:1}" == "~" ]]; then
prefix="~"
p="${p:1}" # drop the ~, leaves "/projects/foo" or ""
fi
# Split remaining path on /
local IFS=/ parts=()
read -ra parts <<< "$p"
# parts[0] is empty (leading slash). Last non-empty part stays full.
local out="" i last=$(( ${#parts[@]} - 1 ))
for (( i=1; i<=last; i++ )); do
local seg="${parts[i]}"
[[ -z "$seg" ]] && continue
if (( i == last )); then
out+="/${seg}"
elif [[ "${seg:0:1}" == "." ]]; then
out+="/${seg:0:2}"
else
out+="/${seg:0:1}"
fi
done
printf '%s%s' "$prefix" "$out"
}
__prompt_dir() {
local slashes upperdirs lastdir
if [[ "$__batColor" != "$__c_lred" ]]; then
slashes=$__batColorSub
upperdirs=$__c_cyn
lastdir=$__c_lcyn
else
slashes=$__c_red
upperdirs=$__c_yel
lastdir=$__c_lred
fi
local pwd_str
pwd_str=$(__prompt_pwd)
# Special cases: just "/" or just "~" — color and return.
if [[ "$pwd_str" == "/" ]]; then
printf '%s/%s' "$slashes" "$__c_nrm"
return
fi
if [[ "$pwd_str" == "~" ]]; then
printf '%s~%s' "$lastdir" "$__c_nrm"
return
fi
# Pull off the last segment.
local final="${pwd_str##*/}"
local rest="${pwd_str%/*}" # everything before the final slash
# Color every / in `rest` and color upperdir letters.
# rest is either "~", "" (path was "/foo"), or "~/a/b" / "/a/b".
local colored_rest=""
if [[ -n "$rest" ]]; then
# Replace each "/" with colored slash, and prefix segments with upperdir color.
# Walk char by char to keep this simple.
local i ch
local prev_was_slash=1 # so the first segment gets upperdir color too
for (( i=0; i<${#rest}; i++ )); do
ch="${rest:i:1}"
if [[ "$ch" == "/" ]]; then
colored_rest+="${slashes}/"
prev_was_slash=1
else
if (( prev_was_slash )); then
colored_rest+="${upperdirs}"
prev_was_slash=0
fi
colored_rest+="$ch"
fi
done
fi
# Now print: colored_rest + colored slash + final segment in lastdir color.
printf '%s%s/%s%s%s' "$colored_rest" "$slashes" "$lastdir" "$final" "$__c_nrm"
}
# ---- Git status ----
__prompt_gitstatus() {
command -v git >/dev/null 2>&1 || return
local branch
branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null) || return
[[ -z "$branch" ]] && return
local dirties
dirties=$(git status --porcelain 2>/dev/null | wc -l)
__color_section "$__c_gry" ":"
__color_section "$__c_lgry" "$branch"
if (( dirties != 0 )); then
__color_section "$__c_gry" ":"
__color_section "$__c_gry" "$dirties"
fi
}
# ---- Last command duration ----
__prompt_proctime() {
[[ -z "$CMD_DURATION" ]] && return
(( CMD_DURATION == 0 )) && return
local lastproctime
if (( CMD_DURATION < 1000 )); then
# under a second: show ms only
lastproctime="${CMD_DURATION}ms"
elif (( CMD_DURATION < 60000 )); then
# under a minute: show seconds and milliseconds, e.g. "31s 569"
local s=$(( CMD_DURATION / 1000 ))
local ms=$(( CMD_DURATION % 1000 ))
lastproctime="${s}s $(printf '%03d' "$ms")"
else
local total_s=$(( CMD_DURATION / 1000 ))
local h=$(( total_s / 3600 ))
local m=$(( (total_s % 3600) / 60 ))
local s=$(( total_s % 60 ))
if (( h > 0 )); then
lastproctime="${h}h ${m}m ${s}s"
else
lastproctime="${m}m ${s}s"
fi
fi
printf ' %s%s%s' "$__batColorSub" "$lastproctime" "$__c_nrm"
}
# ---- Assemble the prompt ----
__build_prompt() {
__timer_stop
local out=""
out+=$(__prompt_section "$(__prompt_battery)")
out+=$(__prompt_section "$(__prompt_load)")
out+=$(__prompt_section "$(__prompt_time)")
out+=$(__prompt_dir)
out+=$(__prompt_gitstatus)
out+=$(__prompt_proctime)
# Wrap all non-printing sequences for bash so line-wrap works.
# Quick-and-dirty: tell bash these escape sequences are zero-width.
PS1="${out//$'\e['/\\[$'\e['}"
PS1="${PS1//m/m\\]}"
PS1+=$'\n'"\\[${__c_nrm}\\]\$ "
}
PROMPT_COMMAND=__build_prompt
# I made these shortcuts to make it comfortable to change colors later.
# Also you can just put set_color manually instead.
set blk (set_color black)
set red (set_color red)
set grn (set_color green)
set yel (set_color yellow)
set blu (set_color blue)
set mgt (set_color magenta)
set cyn (set_color cyan)
set lgry (set_color white)
set gry (set_color black --bold)
set lred (set_color red --bold)
set lgrn (set_color green --bold)
set lyel (set_color yellow --bold)
set lblu (set_color blue --bold)
set lmgt (set_color magenta --bold)
set lcyn (set_color cyan --bold)
set wht (set_color white --bold)
set nrm (set_color normal)
# These are variables indicating the battery's remaining time.
# If there are no battery, these will be left as `$nrm`, which has no color set.
# Think these as a 'default' color value for the prompt and change them as you feel like.
set -g batColor $nrm
set -g batColorSub $nrm
function prompt_section -d "prints a section with a following comma"
if [ $argv[1] != "" ]
printf $argv[1]
printf "$batColorSub, $nrm"
end
end
function color_section -d "prints a section covered in a desired color"
printf $argv[1]
printf $argv[2]
printf $nrm
end
function prompt_battery -d "shows battery status"
# Store the data of battery status, percentage, estimated time left.
set -l str (acpi | grep -oP 'Full|Charging|Discharging|\d+%|\d+:\d+' | tr --truncate-set1 '\n' ' ' | read batStatus batPercent batTime)
# acpi doesn't show time left around the moment of plugging/unplugging AC adapter. Handle this case.
if [ $batTime = "" ]
set batTime "===="
end
if [ $batStatus = "Charging" ]
# if charging, show percentage.
set batColor $lblu
set batColorSub $blu
color_section $batColor (printf '%3s' (echo $batPercent | sed "s/%//"))
color_section $batColorSub "%%%%"
else if [ $batStatus = "Full" ]
# if full, show it's full.
set batColor $lcyn
set batColorSub $cyn
#set batTime $batColor"FULL"$nrm
color_section $batColor "FULL"
else
# if discharging, set color according to how much the time left.
if [ $batTime = "====" ]
# paint it with a color when the time is not stored.
set batColor $lgry
set batColorSub $lgry
color_section $batColorSub "===="
else
# if time presents, convert it in minutes.
set -l batTimeSeg (echo $batTime | sed "s/^0*//" | sed "s/:/\n/" | tr -d ' ')
if [ $batTimeSeg[1] = "" ]
set batTimeSeg[1] 0
end
set -l timeMin (math "$batTimeSeg[1]*60+$batTimeSeg[2]")
if [ $timeMin -le "10" ]
# less than 10 minutes left. Color it red.
set batColor $lred
set batColorSub $red
else if [ $timeMin -le "30" ]
# less than 30 minutes left. Color it yellow.
set batColor $lyel
set batColorSub $yel
else
# more than 30 minutes left. Color it green.
set batColor $lgrn
set batColorSub $grn
end
if [ $timeMin -lt "300" ]
# only show the time when acpi calculation is stabilized.
# estimated maximum battery time for my device is 5 hours, or 300 minutes.
color_section $batColor $batTimeSeg[1]
color_section $batColorSub ":"
color_section $batColor $batTimeSeg[2]
else
# if the calculation is not stabled, show this placeholder instead.
color_section $batColorSub "===="
end
end
end
end
function prompt_load -d "shows loadavg"
# It's known that the load being high can affect battery time left.
# So let it show this information only when it's high enough to affect the battery.
set loadMin (echo (uptime | grep -o '[0-9]\+\.[0-9]\+' | head -n1))
# Threshold I set is 0.60. 1.00 would works.
if [ (math $loadMin \* 100 /1) -gt 60 ]
# color the information according to the battery level.
if [ $batColor = $lred ]
color_section (set_color -b red; set_color black) $loadMin
else if [ $batColor = $lyel ]
color_section $red $loadMin
else
color_section $yel $loadMin
end
else
# or don't show anything if it's not high enough.
echo ""
end
end
function prompt_time -d "prints the time"
date "+$batColorSub%H:%M:%S$nrm"
end
function prompt_dir -d "prints the current directory"
set -l slashes $red
set -l upperdirs $yel
set -l lastdir $lred
if [ $batColor != $lred ]
# use different color when battery is not low.
set slashes $batColorSub
set upperdirs $cyn
set lastdir $lcyn
else
end
printf $upperdirs(prompt_pwd | sed "s/\//$slashes\/$upperdirs/g" | sed "s/\(.*\)\/[^m]*m/\1\/$lastdir/")$nrm
end
function prompt_gitstatus -d "show git status"
set -l branch (git rev-parse --abbrev-ref HEAD ^ /dev/null)
if [ $branch ]
# when the directory is a git repository, show the branch name.
set -l dirties (git status --porcelain | wc -l)
color_section $gry ":"
color_section $lgry $branch
if [ $dirties != 0 ]
# when there is any modified file, show the amount as well.
color_section $gry ":"
color_section $gry $dirties
end
else
# or don't show anything if it's not a git repository.
echo -n ""
end
end
function prompt_proctime -d "show the time taken for the last command"
if [ $CMD_DURATION ] # I think this line is unnecessary, but my computer emits an error without it.
if [ $CMD_DURATION -ne 0 ] # show the time only when there was actual last command.
# show the time in ms.
set -l lastproctime $CMD_DURATION"ms"
if [ $CMD_DURATION -ge 1000 ]
# if it is longer than a single second, show the time in seconds.
set lastproctime (math $CMD_DURATION/1000)"s"
end
color_section $nrm " "
color_section $batColorSub $lastproctime
end
end # this line is unnecessary too.
end
function fish_prompt
# the `if command` line checks if a command exists. Will not execute if it doesn't exist.
# If it's not a laptop there won't be `acpi`. If it's a fresh install `git` won't be as well.
if command -v acpi >/dev/null 2>&1
prompt_section (prompt_battery)
end
prompt_section (prompt_load)
prompt_section (prompt_time)
prompt_dir
if command -v git >/dev/null 2>&1
prompt_gitstatus
end
prompt_proctime
printf \n$nrm\$" "
end
@Dinir
Copy link
Copy Markdown
Author

Dinir commented Apr 6, 2016

newfishprompt

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment