Looks like no one’s replied in a while. To start the conversation again, simply ask a new question.

Terminal wraps long $PS1 prompt line over itself

I found that when $PROMPT_COMMAND or $PS1 are set with grouped colours, e.g. in ~/.bash_profile:


export PS1='\u@\h \[\e[33m\]\w\[\e[37m\] some text \$ \[\e[m\]'


and when the prompt string is very long (e.g. when the working dir is long), then if the line breaks on some sweet spot within (or on or directly next to) the \[grouping brackets\] the line will not wrap around to the next line, but stay on the current line and incorrectly overwrite the first characters of the prompt and possibly incorrectly colour following characters. The problem doesn't occur consistently, but the more colours you add to the prompt string, the easier they seem to occur.


I use posh-git-sh, which dynamically changes $PS1 to contain a prefix, a coloured Git string (if in a Git repo) and a suffix. I thought it was an issue with the implementation of some piece of code within that repo, so I opened and issue: see here. There I've posted screenshots too. Together with de repo's dev I've almost certainly ruled out posh-git-sh being the cause, but instead it could be the MacOS Terminal. The reason I mention it here is because posh-git-sh uses quite a lot of colours and if the line wraps somewhere near the end of the posh git string, the problem occurs. Try it out for yourself and resize the Terminal window, you'll see what happens (use clear after resizing).


I've searched the forums here and found that other people who modified their $PROMPT_COMMAND or $PS1 variables have similar issues when they type in long commands. This is not the problem I'm experiencing! I haven't found anyone mentioning the prompt itself being wrapped incorrectly.

MacBook Pro with Retina display, macOS Sierra (10.12.1), Terminal 2.7.1 (387), bash-4.4.5(1)

Posted on Dec 14, 2016 1:56 AM

Reply
Question marked as Best reply

Posted on Jan 10, 2017 1:57 AM

I can reproduce this with Terminal (macOS Sierra).


But it does not happen with iTerm2

<https://www.iterm2.com/>

You might consider switching terminal emulators


You could file a bug

BugReporter (Free ADC (Apple Developer Connection) account needed for BugReporter

<http://bugreporter.apple.com>

Anyone can get a free ADC account at:

<https://developer.apple.com/register/index.action>


Or you could do what I do, which is just display the last 3 subdirectories, which is generally enough to remind me where I am in the directory tree.


#
# __cwd() - function to create current working directory prompt string.  I
#           want less than the entire path, and more than just the last
#           element in the path.  It turns out the bash prompt string can
#           execute a script function, so I created my own bit of code to
#           generate the current working directory prompt string I want.
#
# Usage:  PS1="...\$(__cwd 3)..."
#
#         Where:   3 in the above example is the number of subdirectories to
#                  keep. 3 is also the default if no value is given.
#         Output:  /
#                  /usr
#                  /usr/share
#                  /usr/share/vim
#                  /.../share/vim/vim63
#                  ~/
#                  ~/Music
#                  ~/Music/iTunes
#                  ~/Music/iTunes/iTunes\ Music  # notice spaces are honored
#                  ~/.../iTunes/iTunes\ Music/Beach Boys
#                  ~/.../iTunes\ Music/Beach\ Boys/Made\ in\ the\ U.S.A.
#
__cwd()
{
    n="${1:-3}"               # if no dir count, default to 3
    p="#${PWD}"               # add # so after split # represents the root dir
    IFS="/"                   # split on directory divider '/'
    set ${p/#\#${HOME}/\~}    # Replace #$HOME with ~ and split into dir parts
    p="${1}"                  # ${1} has # or ~, save it
    shift $(($#-(n+1) < 0 ? 0 : $#-(n+1)))   # remove excess leading dirs
    [[ "${1}" != [#~]* ]] && p="${p}/..."    # ... represents a partial path
    for ((j=2; j<n+2; j++)); do p="${p}/${!j}"; done # append last n dirs
    while [[ "${p}" = [#/]/* ]]; do p="${p#?}"; done # del leading /'s and #
    while [[ "${p}" = *[^~]/ ]]; do p="${p%?}"; done # del trailing /'s
    [[ ! -z ${ADE_VIEW_NAME} ]] && p="${p//${ADE_VIEW_NAME}\//@view/}" # view shorthand
    if [[ "${p}" = [~]* ]]; then
        printf "~%q" "${p#?}" # generate path str w/shell magic chars quoted
    else
        printf "%q" "${p}"    # generate path str w/shell magic chars quoted
    fi
}

export PS1='\u@\h \[\e[33m\]$(__cwd 3)\[\e[37m\] some text \$ \[\e[m\]'


This works because the $(__cwd 3) is processed each and every time the PS1 string is used, so I can dynamically generate my current working directory string for each prompt


I have an even longer prompt string

echo $PS1
\[$(tab-color green)\]\[\e]0;BobTouchBar:$(__cwd 3)\007\]\D{%d%a@%R}[\[\e[44;36;1m\]Bob\[\e[44;31;1m\]Touch\[\e[44;32;1m\]Bar\[\e[0m\]]\[\e[34;1m\]$(__cwd 3)\[\e[0m\]>


The $(tab-color ...) is a function I created to work with iTerm2 features. The \e]0 sets the title bar, and then I mix colors left/right and side ways 🙂

6 replies
Question marked as Best reply

Jan 10, 2017 1:57 AM in response to erikieperikie

I can reproduce this with Terminal (macOS Sierra).


But it does not happen with iTerm2

<https://www.iterm2.com/>

You might consider switching terminal emulators


You could file a bug

BugReporter (Free ADC (Apple Developer Connection) account needed for BugReporter

<http://bugreporter.apple.com>

Anyone can get a free ADC account at:

<https://developer.apple.com/register/index.action>


Or you could do what I do, which is just display the last 3 subdirectories, which is generally enough to remind me where I am in the directory tree.


#
# __cwd() - function to create current working directory prompt string.  I
#           want less than the entire path, and more than just the last
#           element in the path.  It turns out the bash prompt string can
#           execute a script function, so I created my own bit of code to
#           generate the current working directory prompt string I want.
#
# Usage:  PS1="...\$(__cwd 3)..."
#
#         Where:   3 in the above example is the number of subdirectories to
#                  keep. 3 is also the default if no value is given.
#         Output:  /
#                  /usr
#                  /usr/share
#                  /usr/share/vim
#                  /.../share/vim/vim63
#                  ~/
#                  ~/Music
#                  ~/Music/iTunes
#                  ~/Music/iTunes/iTunes\ Music  # notice spaces are honored
#                  ~/.../iTunes/iTunes\ Music/Beach Boys
#                  ~/.../iTunes\ Music/Beach\ Boys/Made\ in\ the\ U.S.A.
#
__cwd()
{
    n="${1:-3}"               # if no dir count, default to 3
    p="#${PWD}"               # add # so after split # represents the root dir
    IFS="/"                   # split on directory divider '/'
    set ${p/#\#${HOME}/\~}    # Replace #$HOME with ~ and split into dir parts
    p="${1}"                  # ${1} has # or ~, save it
    shift $(($#-(n+1) < 0 ? 0 : $#-(n+1)))   # remove excess leading dirs
    [[ "${1}" != [#~]* ]] && p="${p}/..."    # ... represents a partial path
    for ((j=2; j<n+2; j++)); do p="${p}/${!j}"; done # append last n dirs
    while [[ "${p}" = [#/]/* ]]; do p="${p#?}"; done # del leading /'s and #
    while [[ "${p}" = *[^~]/ ]]; do p="${p%?}"; done # del trailing /'s
    [[ ! -z ${ADE_VIEW_NAME} ]] && p="${p//${ADE_VIEW_NAME}\//@view/}" # view shorthand
    if [[ "${p}" = [~]* ]]; then
        printf "~%q" "${p#?}" # generate path str w/shell magic chars quoted
    else
        printf "%q" "${p}"    # generate path str w/shell magic chars quoted
    fi
}

export PS1='\u@\h \[\e[33m\]$(__cwd 3)\[\e[37m\] some text \$ \[\e[m\]'


This works because the $(__cwd 3) is processed each and every time the PS1 string is used, so I can dynamically generate my current working directory string for each prompt


I have an even longer prompt string

echo $PS1
\[$(tab-color green)\]\[\e]0;BobTouchBar:$(__cwd 3)\007\]\D{%d%a@%R}[\[\e[44;36;1m\]Bob\[\e[44;31;1m\]Touch\[\e[44;32;1m\]Bar\[\e[0m\]]\[\e[34;1m\]$(__cwd 3)\[\e[0m\]>


The $(tab-color ...) is a function I created to work with iTerm2 features. The \e]0 sets the title bar, and then I mix colors left/right and side ways 🙂

Jan 10, 2017 2:03 AM in response to BobHarris

I'd use the "This solved my question" button, but it does nothing for me. Possibly some script being blocked by my browser, but I don't have the time to look into it.


Thanks for the answer. It is indeed a solution to limit the max num of dirs being displayed in the prompt. No need for me to see much more than three. Nice piece of code!


I think that in time I may use a different terminal emulator, e.g. iTerm2, so I won't have this problem. Your solution is great for Terminal.app.

Aug 2, 2017 9:29 AM in response to BobHarris

I was struggling with the same problem and your post allowed me to find a root cause. Indeed it was related to the colour definition in PS1 but iTerm2 was affected too in my case.


The following setting was causing the problem: export PS1="\e[0;34m\h:\W\e[m \$ "


and the following solved it: export PS1="\[$(tput sgr0)\]\[\033[38;5;31m\]\h:\W\[$(tput sgr0)\]\$ "


The difference is in colour definition.


I leave it to the readers to dig into details but apparently the "\e[" notation is the culprit.


I hope it helps someone.


Cheers,

Jacek

Aug 2, 2017 10:29 AM in response to jjaroczy

The following setting was causing the problem: export PS1="\e[0;34m\h:\W\e[m \$ "


and the following solved it: export PS1="\[$(tput sgr0)\]\[\033[38;5;31m\]\h:\W\[$(tput sgr0)\]\$ "

The biggest change I can see is the use of \[ and \] to tell the shell that the sequence inside is NOT part of the printed text, so the shell will properly calculate the visible length of the prompt.


But there is nothing wrong with \033 as an alternative to \e


...but iTerm2 was affected too in my case.

Without the \[ \] notation, it is the shell that thinks your prompt is physically longer than it really is, and that can confuse the shell's ability to correctly do command line editing. It would not matter what terminal emulator you were using.

Aug 2, 2017 11:12 AM in response to jjaroczy

I keep the escape sequences to a minimum with the following:


# White="\[\e[0;38;5;15m"

# Bluebld="\[\e[0;38;5;63m"

Bluebld="$(tput bold; tput setaf 63)"

White="$(tput bold; tput setaf 15)"

Green="$(tput bold; tput setaf 42)"

Normal="$(tput sgr0)"

MyHost="$(networksetup -getcomputername)"

export PS1='\[$Bluebld\]$MyHost: \w\$ \[$Green\]'


odin: ~$ echo $PS1

\[$Bluebld\]$MyHost: \w\$ \[$Green\]

Terminal wraps long $PS1 prompt line over itself

Welcome to Apple Support Community
A forum where Apple customers help each other with their products. Get started with your Apple ID.