Setting value of command prompt ( PS1) based on present directory string length


I know I can do this to reflect just last 2 directories in the PS1 value.


but lets say we have a directory name that’s really messy and will reduce my working space , like




those are the last 2 directories before the prompt

I want to set a condition if directory length of either of the last 2 directories is more than a threshold e.g. 10 chars , shorten the name with 1st 3 and last 3 chars of the directory (s) whose length exceeds 10

    2021-07-23--07-48-49_xperia-build-20191119010027  & 


both gt 10 will be shortened to 202*027 & PS1 will be respectively

T-Mob/202*027/# for T-Mob/2021-07-23--07-48-49_xperia-build-20191119010027# and
202*027/T-Mob# for 2021-07-23--07-48-49_nokia-build-20191119010027/T-Mob#

A quick 1 Liner to get this done ?
I cant post this in comments so Updating here. Ref to Joaquins Answer ( thx J)

PS1=''`echo ${PWD#"${PWD%/*/*}/"} | awk -v RS='/' 'length()  <=10{printf $0"/"}; length()>10{printf "%s*%s/", substr($0,1,3),  substr($0,length()-2,3)};'| tr -d "\n"; echo "#"`''

see below o/p’s

/root/my-applications/bin # it shortened as expected
my-*ons/bin/#cd -  # going back to prev.
my-*ons/bin/#    #value of prompt is the same but I am in /root


A one-liner is basically always the wrong choice. Write code to be robust, readable and maintainable (and, for something that’s called frequently or in a tight loop, to be efficient) — not to be terse.

Assuming availability of bash 4.3 or newer:

# Given a string, a separator, and a max length, shorten any segments that are
# longer than the max length.
shortenLongSegments() {
  local -n destVar=$1; shift  # arg1: where do we write our result?
  local maxLength=$1; shift   # arg2: what's the maximum length?
  local IFS=$1; shift         # arg3: what character do we split into segments on?
  read -r -a allSegments <<<"$1"; shift        # arg4: break into an array

  for segmentIdx in "${!allSegments[@]}"; do   # iterate over array indices
    segment=${allSegments[$segmentIdx]}        # look up value for index
    if (( ${#segment} > maxLength )); then     # value over maxLength chars?
      segment="${segment:0:3}*${segment:${#segment}-3:3}" # build a short version
      allSegments[$segmentIdx]=$segment        # store shortened version in array
  printf -v destVar '%s\n' "${allSegments[*]}" # build result string from array

# function to call from PROMPT_COMMAND to actually build a new PS1
buildNewPs1() {
  # declare our locals to avoid polluting global namespace
  local shorterPath
  # but to cache where we last ran, we need a global; be explicit.
  declare -g buildNewPs1_lastDir
  # do nothing if the directory hasn't changed
  [[ $PWD = "$buildNewPs1_lastDir" ]] && return 0
  shortenLongSegments shorterPath 10 / "$PWD"
  # update the global tracking where we last ran this code

PROMPT_COMMAND=buildNewPs1 # call buildNewPs1 before rendering the prompt

Note that printf -v destVar %s "valueToStore" is used to write to variables in-place, to avoid the performance overhead of var=$(someFunction). Similarly, we’re using the bash 4.3 feature namevars — accessible with local -n or declare -n — to allow destination variable names to be parameterized without the security risk of eval.

If you really want to make this logic only apply to the last two directory names (though I don’t see why that would be better than applying it to all of them), you can do that easily enough:

buildNewPs1() {
  local pathPrefix pathFinalSegments
  pathPrefix=${PWD%/*/*}           # everything but the last 2 segments
  pathSuffix=${PWD#"$pathPrefix"}  # only the last 2 segments

  # shorten the last 2 segments, store in a separate variable
  shortenLongSegments pathSuffixShortened 10 / "$pathSuffix"

  # combine the unshortened prefix with the shortened suffix

…adding the performance optimization that only rebuilds PS1 when the directory changed to this version is left as an exercise to the reader.

Answered By – Charles Duffy

This Answer collected from stackoverflow, is licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply

(*) Required, Your email will not be published