Last active
May 4, 2026 12:40
-
-
Save Dinir/a344affcad551dcbb248 to your computer and use it in GitHub Desktop.
My Prompt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # ~/.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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # 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 |
Author
Dinir
commented
Apr 6, 2016

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