I've just moved from tcsh
to bash
and I particular missed the directory-shortening prompt options using %c02
(with ellipsis
also set).
I see that PROMPT_DIRTRIM
does almost the right thing (except ellipsis, I think) but I'm only on bash
3 (on OS X). I found this recipe elsewhere on SO, which shortens on total length, so breaks pathnames in the middle of directories, which I didn't like.
So I came up with this:
PROMPT_DIRTRIM=2 ## from bash4, but used here
dirtrim()
{
local NAME="$1" start= endelts=
[[ "$NAME" =~ ^"$HOME"(/|$) ]] && NAME="~${NAME#$HOME}" ## $HOME ==> ~
IFS=/ read -ra elts <<< "$NAME"; ## split $PWD on "/"
start=$((${#elts[@]}-${PROMPT_DIRTRIM})) ## first element to retain
if [ ${start} -gt 1 ]; then
for ((i=${start}; i<${#elts[@]}; i++)); do
endelts="${endelts}/${elts[$i]}"; ## concat together the trailing path
done
NAME="...${endelts}"
fi
echo "$NAME"
}
PS1='\h:$(dirtrim "$PWD")\$ '
It works:
blackat:~$ cd ~/Library/Application\ Support/Apple
blackat:.../Application Support/Apple$
But I'm newish to bash
and unhappy with the explicit for (())
loop; however, I couldn't seem to find any other way to rejoin the last entries of the split elts
array in a way that deals correctly with spaces in dir names (e.g., using ${elts[@]:${start}}
). Any hints to do that or other improvements?
(By the way, I think this is a programming question, to the extent that bash
is a programming language....)
Use PUSHD , CD , and POPD to go back and forth to a specific path in order to temporarily use a shorter relative path.
Just enter PS1='\u:\W\$ ' and press enter.
The default BASH prompt is the one you see when you first open a terminal or command line. It usually looks something like this: username@hostname:~$ Alternatively, it may look like this: (base) [username@localhost ~]$ The first part of the prompt tells you the user that's currently logged in.
You could try this:
if ((start > 1)); then
name=$(IFS=/; echo .../"${elts[*]:start}")
# If your terminal is correctly set up for unicode, you can save two character positions:
# name=$(IFS=/; echo …/"${elts[*]:start}")
fi
Note that in bash, in an arithmetic context, which includes the inside of ((...))
and array subscripts, you can write just the name of the variable; no need for sigils.
Another way to do it would be
if ((start > 1)); then
printf -v name "/%s" "${elts[@]:start}"
name=...$name
fi
Yet another solution, using regex captures in the BASH_REMATCH
array rather than splitting and rejoining the string:
dirtrim () {
local path="$1";
[[ $path =~ ^"$HOME"(/.*)? ]] && path=~${BASH_REMATCH[1]};
((PROMPT_DIRTRIM)) &&
[[ $path =~ ...*((/[^/]*){$PROMPT_DIRTRIM}) ]] &&
path=…${BASH_REMATCH[1]};
echo "$path"
}
The ((PROMPT_DIRTRIM))
test is not completely robust because of the peculiarities of bash evaluation in an arithmetic context. For distribution you might prefer something like [[ $PROMPT_DIRTRIM =~ ^[1-9][0-9]*$ ]]
Not really an answer, but you might want to to look at how mksh
accomplishes this:
PS1=${| local e=$? (( e )) && REPLY+="$e|" REPLY+=${USER:=$(ulimit -c 0; id -un 2>/dev/null || echo \?)} REPLY+=@${HOSTNAME%%.*}: local d=${PWD:-?} p=~; [[ $p = ?(*/) ]] || d=${d/#$p/~} local m=${%d} n p=...; (( m > 0 )) || m=${#d} (( m > (n = (COLUMNS/3 < 7 ? 7 : COLUMNS/3)) )) && d=${d:(-n)} || p= REPLY+=$p$d return $e }
Unfortunately, it uses some extensions that are not in bash
, I believe.
Aso, since you're switching shells... have you considered fish?
It does this out-of-the-box, and then some.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With