How to expand PS1?

l0b0 picture l0b0 · Aug 10, 2010 · Viewed 12.3k times · Source

I have a shell script that runs the same command in several directories (fgit). For each directory, I would like it to show the current prompt + the command which will be run there. How do I get the string that corresponds to the decoded (expanded)PS1? For example, my default PS1 is

${debian_chroot:+($debian_chroot)}\[\e[1;32m\]\u\[\e[0m\]@\[\e[1;32m\]\h\[\e[0m\]:\[\e[1;34m\]\w\[\e[0m\]$(__git_ps1 ' (%s)')$

and I'd like to echo the resulting prompt username@hostname:/path$, preferably (but not necessarily) with the nice colors. A cursory look at the Bash manual didn't reveal any definite answer, and echo -e $PS1 only evaluates the colors.

Answer

gniourf_gniourf picture gniourf_gniourf · May 10, 2016

Since Bash 4.4 you can use the @P expansion:

First I put your prompt string in a variable myprompt using read -r and a quoted here-doc:

read -r myprompt <<'EOF'
${debian_chroot:+($debian_chroot)}\[\e[1;32m\]\u\[\e[0m\]@\[\e[1;32m\]\h\[\e[0m\]:\[\e[1;34m\]\w\[\e[0m\]$(__git_ps1 ' (%s)')$ 
EOF

To print the prompt (as it would be interpreted if it were PS1), use the expansion ${myprompt@P}:

$ printf '%s\n' "${myprompt@P}"
gniourf@rainbow:~$
$

(In fact there are some \001 and \002 characters, coming from \[ and \] that you can't see in here, but you can see them if you try to edit this post; you'll also see them in your terminal if you type the commands).


To get rid of these, the trick sent by Dennis Williamson on the bash mailing list is to use read -e -p so that these characters get interpreted by the readline library:

read -e -p "${myprompt@P}"

This will prompt the user, with the myprompt correctly interpreted.

To this post, Greg Wooledge answered that you might as well just strip the \001 and \002 from the string. This can be achieved like so:

myprompt=${myprompt@P}
printf '%s\n' "${myprompt//[$'\001'$'\002']}"

To this post, Chet Ramey answered that you could also turn off line editing altogether with set +o emacs +o vi. So this will do too:

( set +o emacs +o vi; printf '%s\n' "${myprompt@P}" )