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

Issue

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

PS1=${PWD#"${PWD%/*/*}/"}#

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

T-Mob/2021-07-23--07-48-49_xperia-build-20191119010027#

OR

2021-07-23--07-48-49_nokia-build-20191119010027/T-Mob#

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
e.g.

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

2021-07-23--07-48-49_nokia-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.
/root
my-*ons/bin/#    #value of prompt is the same but I am in /root

Solution

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
    fi
  done
  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"
  PS1="${shorterPath}\$"
  # update the global tracking where we last ran this code
  buildNewPs1_lastDir=$PWD
}

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
  PS1="${pathPrefix}${pathSuffixShortened}\$"
}

…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