Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to expand PS1?

Tags:

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.

like image 519
l0b0 Avatar asked Aug 10 '10 18:08

l0b0


People also ask

What is PS1 variable in Linux?

PS1 – This is the primary prompt display. This is where you set special characters or important information. PS2 – This is the secondary prompt string. This is usually set as a divider between the prompt display and the text entry. It is also used to display when a long command is broken into sections with the \ sign.

How do I permanently set a PS1 variable in Linux?

In Linux, the PS1 system variable is used to change the prompt setting. To change the Linux prompt permanently, you need to modify the . bashrc file which initializes the interactive shell session whenever the system starts.

How do I change bash prompt?

To change your Bash prompt, you just have to add, remove, or rearrange the special characters in the PS1 variable. But there are many more variables you can use than the default ones. Leave the text editor for now—in nano, press Ctrl+X to exit.

How do I get to the command prompt in Linux?

You can launch the terminal shell prompt in one step by using the "Ctrl-Alt-T" keyboard shortcut. When you are done with the terminal, you can let it run minimized or exit it completely by clicking the "Close" button.


2 Answers

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}" ) 
like image 56
gniourf_gniourf Avatar answered Oct 15 '22 03:10

gniourf_gniourf


One great advantage of open source software is that the source is, well, open :-)

Bash itself does not provide this functionality but there are various tricks you can use to provide a subset (such as substituting \u with $USER and so on). However, this requires a lot of duplication of functionality and ensuring that the code is kept in sync with whatever bash does in future.

If you want to get all the power of prompt variables (and you don't mind getting your hands dirty with a bit of coding (and, if you do mind, why are you here?)), it's easy enough to add to the shell itself.

If you download the code for bash (I'm looking at version 4.2), there's a y.tab.c file which contains the decode_prompt_string() function:

char *decode_prompt_string (string) char *string; { ... } 

This is the function that evaluates the PSx variables for prompting. In order to allow this functionality to be provided to users of the shell itself (rather than just used by the shell), you can follow these steps to add an internal command evalps1.

First, change support/mkversion.sh so that you won't confuse it with a "real" bash, and so that the FSF can deny all knowledge for warranty purposes :-) Simply change one line (I added the -pax bit):

echo "#define DISTVERSION \"${float_dist}-pax\"" 

Second, change builtins/Makefile.in to add a new source file. This entails a number of steps.

(a) Add $(srcdir)/evalps1.def to the end of DEFSRC.

(b) Add evalps1.o to the end of OFILES.

(c) Add the required dependencies:

evalps1.o: evalps1.def $(topdir)/bashtypes.h $(topdir)/config.h \            $(topdir)/bashintl.h $(topdir)/shell.h common.h 

Third, add the builtins/evalps1.def file itself, this is the code that gets executed when you run the evalps1 command:

This file is evalps1.def, from which is created evalps1.c. It implements the builtin "evalps1" in Bash.  Copyright (C) 1987-2009 Free Software Foundation, Inc.  This file is part of GNU Bash, the Bourne Again SHell.  Bash is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.  Bash is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more details.  You should have received a copy of the GNU General Public License along with Bash.  If not, see <http://www.gnu.org/licenses/>.  $PRODUCES evalps1.c  $BUILTIN evalps1 $FUNCTION evalps1_builtin $SHORT_DOC evalps1 Outputs the fully interpreted PS1 prompt.  Outputs the PS1 prompt, fully evaluated, for whatever nefarious purposes you require. $END  #include <config.h> #include "../bashtypes.h" #include <stdio.h> #include "../bashintl.h" #include "../shell.h" #include "common.h"  int evalps1_builtin (list)      WORD_LIST *list; {   char *ps1 = get_string_value ("PS1");   if (ps1 != 0)   {     ps1 = decode_prompt_string (ps1);     if (ps1 != 0)     {       printf ("%s", ps1);     }   }   return 0; } 

The bulk of that is the GPL licence (since I modified it from exit.def) with a very simple function at the end to get and decode PS1.

Lastly, just build the thing in the top level directory:

./configure make 

The bash executable that appears can be renamed to paxsh, though I doubt it will ever become as prevalent as its ancestor :-)

And running it, you can see it in action:

pax> mv bash paxsh  pax> ./paxsh --version GNU bash, version 4.2-pax.0(1)-release (i686-pc-linux-gnu) Copyright (C) 2011 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>  This is free software; you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law.  pax> ./paxsh  pax> echo $BASH_VERSION 4.2-pax.0(1)-release  pax> echo "[$PS1]" [pax> ]  pax> echo "[$(evalps1)]" [pax> ]  pax> PS1="\h: "  paxbox01: echo "[$PS1]" [\h: ]  paxbox01: echo "[$(evalps1)]" [paxbox01: ] 

When you put one of the PSx variables into the prompt, echoing $PS1 simply gives you the variable, while the evalps1 command evaluates it and outputs the result.

Now, granted, making code changes to bash to add an internal command may be considered by some to be overkill but, if you want an perfect evaluation of PS1, it's certainly an option.

like image 29
paxdiablo Avatar answered Oct 15 '22 04:10

paxdiablo