Is there a way that I can determine the length of a prompt? Is there a way that I can look at the PS1 variable and get the length from it and repeat characters for the same length?

I am trying to create a new terminal prompt for the bash command.

  • The length of a prompt is going to be dynamic, depending on the variables you're using, such as user, directory, host and so on. Please give some specific example. What's your prompt, and what's your expectation. You can check by echo $PS1. Also, what you're trying to do exactly. – kenorb Oct 29 '19 at 18:32
  • So is there a way that I can look at the PS1 var and get the Length from it and repeat chars for the same length? – Bill Daugherty Oct 29 '19 at 18:33
  • What's your goal? What do you want your prompt to look like? – glenn jackman Oct 29 '19 at 20:08

If your PS1 is well written then it encloses non-printing fragments in \[ \]. You need to get rid of these fragments first. Normally I would use sed but I don't think one can easily do non-greedy matching with sed, hence perl:

perl -pe 's|\\\[.*?\\\]||g' <<<"$PS1"

A newline inside \[ \] would make perl not remove the fragment. This shouldn't happen with well written PS1, so it's not a problem.

You need the result in a helper variable:

ps="$(perl -pe 's|\\\[.*?\\\]||g' <<<"$PS1")"

A separate variable is needed because the next step is to ask Bash to evaluate the string as if it was a prompt; this is easy with a variable.

One thing though: here string (<<<) ads a trailing newline, then command substitution ($( )) removes all trailing newlines. If originally there are no newlines then both phenomena will cancel each other. If there are trailing newlines then you will lose them all and the final result will be off by an unknown number. In a general case we can deal with this by appending a suffix (here X):

ps="$(perl -pe 's|\\\[.*?\\\]||g' <<<"${PS1}X")"

This way both mechanisms cancel each other for sure. Then you can evaluate as if it was a prompt, like this:

printf '%s' "${ps@P}"

You need the length anyway, so printf is not exactly necessary:

wc -m <<<"${ps@P}"

I think wc -m is better than wc -c in this context. The result is inflated because of our suffix (of length 1) and yet another additional newline (from <<<). Therefore you need to subtract 2.

The whole procedure as a function:

plen () {
   local ps len
   ps="$(perl -pe 's|\\\[.*?\\\]||g' <<<"${PS1}X")"
   len="$(wc -m <<<"${ps@P}")"
   printf '%s\n' "$((len-2))"

You said you want to

repeat chars for the same length

Maybe you don't need length for this.

pblock () {
   local ps
   ps="$(perl -pe 's|\\\[.*?\\\]||g' <<<"${PS1}X")"
   printf '%s' "${ps::-1}" | tr -c '\n\r' 'M'

This function will replicate the shape of the prompt even if it's multi-line, although characters like tab are troublesome. Depending on your tr multi-byte characters (e.g. ś) may also make the function not work as expected.

You may or may not prefer printf '%s\n' ….

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.