bash prompt shell displayed differently in terminal and tty console - bash

This is a portion of my ~/.bashrc:
prompt(){
local EXIT="$?" # return code
PS1=""
local red="\[\033[0;31m\]" # text colour
local purple="\[\033[0;35m\]" # text colour
local normal="\[\033[0m\]" # text colour
if [ $EXIT == 0 ]; then # $EXIT colour based upon its value
local return="${normal}${?}"
else
local return="${red}${?}${normal}"
fi
PS1+="${normal}[${purple}\\D{%-l:%M%P}${normal}]${return} \\[\\e]0; \
\\u#\\h: \\w\\a\\]${debian_chroot:+($debian_chroot)}\\u#\\h:\\w\\$ "
}
export PROMPT_COMMAND=prompt
This is my prompt shell in gnome-terminal (correctly displayed):
[5:01pm]0 user#host:~$
But when I switch to tty console, after I logged in, this is displayed:
[5:05pm]0 ;user#host: ~user#host:~$
This happens with regular user, but also with root on the same notebook on the same operating system.
Colours are always correctly displayed, the only problem is with the way the prompt is displayed.
Sofware versions:
GNU bash, version 4.3.11(1)-release (i686-pc-linux-gnu),
Ubuntu Gnome 14.04.
Some explanation of the PS1 value:
[5:01pm] # current time
0 # return/exit code of the last command (0 can be any number;
# if return code has a non-zero value, it turns red)
PS – Currently in tty consoles, I need to source ~/.bashrc to be able to used all of my settings. (1) Where is located the tty console’s own .bashrc? (2) Or how to set it up to use the ~/.bashrc?
Solution of the different behaviour of prompt
As the accepted answer suggests, in PS1 variable, there was a part which should be omitted, thus I just changed the last row of the function to
PS1+="${normal}[${purple}\\D{%-l:%M%P}${normal}]${return} \
${debian_chroot:+($debian_chroot)}\\u#\\h:\\w\\$ "
Solution of the Post Scriptum (PS)
tty consoles use ~/.bashrc_profile instead of ~/.bashrc, because (as #chepner said in comment below) the latter is sourced by GUI terminal emulator (for it usually starts a non-login interactive shell). tty console is an interactive shell.
My solution is to add the following to ~/.bash_profile:
. ~/.bashrc

You have \\[\\e]0; \\u#\\h: \\w\\a\\] and \\u#\\h:\\w\\$ in PS1.
The question is why is your terminal not showing that first set but your console is.
The answer, I believe, is that you have that first set enclosed in a \[...\] block which indicates that it is non-printing and takes up no space (this is why you need to enclose color codes in \[...\] to avoid the prompt being shorter than the terminal expects when the codes don't create visible characters.
This is causing gnome-terminal to discard everything (even visible characters) from the output/contents of the \[...\] block.
The console, presumably, is simply printing visible characters (and ignoring non-printing characters). (I wonder if this causes prompt size miscalculations or not.)
The solution here is to remove that first (seemingly unintentional) set of escapes.

Related

iTerm2 : Path into PS1 prompt is broken (\n) on different lines when it is too long

I am using iTerm2 on MacOS Catalina. Here is my PS1 into ~/.bashrc, using a function ps1 :
function ps1 {
# PROMPT SECTION
BLUE='\[\033[0;34m\]'
LIGHT_CYAN='\[\033[1;36m\]'
DEFAULT='\[\033[0m\]'
PURPLE='\[\033[0;35m\]'
LIGHT_PURPLE='\[\033[1;35m\]'
LIGHT_GREEN='\[\033[0;32m\]'
PINK='\[\033\[0;31m\]'
# \! - History number of last command
# $? - Exit value of last command
# \t - Exit time of last command
# \w - Current directory (relative)
PS1="$PURPLE|$DEFAULT$LIGHT_GREEN\u#$DEFAULT$LIGHT_CYAN\h$DEFAULT$PURPLE|$DEFAULT\t$PURPLE|$DEFAULT$LIGHT_PURPLE\w$DEFAULT$PURPLE|$DEFAULT "
}
ps1
The issue happens when I have long path, it seems to be broken on second or third line like this:
I tried to increase the number of columns (to 1000) into iTerm2 -> Preferences but it doesn't seem to fix this issue.
I can't see where the error comes from. What might be wrong?
fab#astro|13:12:32|~/Phd_2019_2020/Travail_2_faire_varier_Omega_DE_sur_CAMB_et_regarder_si_les_P_k_changent_21_Octobre_2019/Main_Directory_Work/TSAF_lvl_up_Derivatives_15_points_on_Cl_stabilite_Code_is_surel
y_VALIDATED_for_15_points_derivatives_AND_After_trying_to_solve_the_pathologic_zone_1e-5_TO_1e-2_13_Novembre_2019/Test_FAIRE_VARIER_TOUS_LES_PARAMETRES_AVEC_les_2_pas_2e-2_et_1e-1_AVEC_Takabird_SECONDE_TENTATIVE_AVEC_LA_VAR IATION_de_wde_EN_FLAT_DANS_Camb_launcher_6_DECEMBRE_2019/CAMB_der_3_pts/fortran|
Update
Solution finally found on this link.
Stopping the word-wrapping on iTerm2 is still an open feature request. So I'm afraid that if your line is too long, it'll get wrapped automatically anyway.
However, you could build your PS1 such that the line won't overflow by trimming the path down.
For that, you'd need the command tput cols to get the number of columns on your terminal if you want to do it manually, or just use one of the answers in there.
If you are open to changing your shell from bash to zsh, a lot of thems in zsh trim the paths so that they don't overflow the line.
Another solution given in this answer would be to use the commands tput rmam to discard output that overflow, then re-enable wrapping with tput smam

What is the name of the $ or # signs that indicates if you're root?

I recently discovered that the # sign indicates that you're root on the shell (at least in a bash shell), and the $ sign indicates that you're not.
What is the name of this sign and does it really have the meaning that I give to it?
Is it only a bash thing?
Example with default bash configuration on Ubuntu:
john#mycomputer /tmp $ echo "I'm a simple user"
mycomputer /tmp # echo "I'm the root user"
There does not appear to be a formal definition of the prompt character indicating your privileges.
This thorough answer to “What is the origin of the UNIX $ (dollar) prompt?” provides lots of historical background, but it does not have a name for this indicator either.
In the absence of an official term, I'd call this the “prompt privilege indicator” or the “prompt indicator symbol” or even just “prompt indicator.”
All shells have a prompt whose default string ends in an indicator symbol, which (by default) almost always indicates whether you are root. Most shells allow you to change the prompt and most shells have a way to specify the prompt indicator symbol.
Here's what you can find in the various shell docs and standards:
A segment of the bash man page:
PROMPTING
When executing interactively, bash displays the primary prompt PS1 when
it is ready to read a command, and the secondary prompt PS2 when it
needs more input to complete a command. Bash displays PS0 after it
reads a command but before executing it. Bash allows these prompt
strings to be customized by inserting a number of backslash-escaped
special characters that are decoded as follows:
…
\$ if the effective UID is 0, a #, otherwise a $
From the zsh man pages:
Shell state
%# A `#' if the shell is running with privileges, a `%' if not.
Equivalent to `%(!.#.%%)'. The definition of `privileged', for
these purposes, is that either the effective user ID is zero,
or, if POSIX.1e capabilities are supported, that at least one
capability is raised in either the Effective or Inheritable
capability vectors.
The POSIX standard doesn't even specify what the privileged prompt symbol should be:
PS1
Each time an interactive shell is ready to read a command, the value of this variable shall be subjected to parameter expansion and written to standard error. The default value shall be $ . For users who have specific additional implementation-defined privileges, the default may be another, implementation-defined value.
See also the dash man page (dash is a popular basic POSIX shell, often installed as /bin/sh):
PS1 The primary prompt string, which defaults to ``$ '', unless you
are the superuser, in which case it defaults to ``# ''.
POSIX and dash don't have anything dynamic in what you can set $PS1 to. The default prompt is set once and is replaced by any assignments to the PS1 variable, losing the ability to distinguish between root and unprivileged users with a single character. You'd have to write your own code to determine if you're privileged and use its output in your PS1 assignment.
It's called a prompt. These days, they can be configured in many different ways. This related question gives some background.

emacs terminal bash (PS1) prompt duplicated

This is a bit of a convoluted question, but here goes nothing!
I've recently updated my bash prompt to the nice version appearing in the last post of this thread: Bash: custom PS1 with nice working directory path.
The relevant bash code (from that thread post) is copied here:
# define the awk script using heredoc notation for easy modification
MYPSDIR_AWK=$(cat << 'EOF'
BEGIN { FS = OFS = "/" }
{
if (length($0) > 16 && NF > 4)
print $1,$2,".." NF-4 "..",$(NF-1),$NF
else
print $0
}
EOF
)
# my replacement for \w prompt expansion
export MYPSDIR='$(echo -n "${PWD/#$HOME/~}" | awk "$MYPSDIR_AWK")'
# the fancy colorized prompt: [0 user#host ~]%
# return code is in green, user#host is in bold/white
export PS1='[\[\033[1;32m\]$?\[\033[0;0m\] \[\033[0;1m\]\u#\h\[\033[0;0m\] $(eval "echo ${MYPSDIR}")]% '
# set x/ssh window title as well
export PROMPT_COMMAND='echo -ne "\033]0;${USER}#${HOSTNAME%%.*} $(eval "echo ${MYPSDIR}")\007"'
This prompt looks roughly like so (in non-emacs terminals):
[0 user#host ~/my_dir]%
Where the "0" above is green and the "user#host" is bold.
(Note that the "0" can be all sorts of numbers, and represents the return value of the last command.)
The issue I'm experiencing is specific to shells running within emacs (and it occurs for most variants of terminal-interaction within emacs: 'term', 'ansi-term', 'shell', and 'eshell').
The prompt appears twice (and slightly broken) in emacs terminals, like so:
0;user#host ~/my_dir[0 user#host ~/my_dir]%
The 'second' version of the prompt, starting from and including the "[" looks just fine.
It's the preceding text, which appears without any styling (i.e. no green and no bold).
So, emacs must be interpreting some portion of the prompt as input, and my guess is the color or bold escaped indicators attached to the "0" and "user#host" portions of the prompt?
Might anyone know how to tell emacs to interpret the escapes correctly?
Or, alternatively, how to modify the prompt-setting commands such that both emacs will not hate it and it'll still work in non-emacs terminals?
And maybe even another alternative: how to add a test for the terminal type ('eterm-color' within emacs) with a modified string that is emacs-friendly?
The error comes from the export PROMPT_COMMAND=... statement.
You can avoid this being read in your configuration, by checking whether you have a shell running inside emacs or not. Here the environment variable INSIDE_EMACS becomes handy. From the Emacs manual (Sect. 32.7):
Emacs sets the environment variable INSIDE_EMACS in the subshell to ‘version,comint’, where version is the Emacs version (e.g., ‘24.1’). Programs can check this variable to determine whether they are running inside an Emacs subshell
In your example, you want
export PROMPT_COMMAND='echo -ne "\033]0;${USER}#${HOSTNAME%%.*} $(eval "echo ${MYPSDIR}")\007" only being executed when you are not in emacs, otherwise you get this nasty "double prompt". The following conditional statement in your code will help.
if [ -z "$INSIDE_EMACS" ];
then
export PROMPT_COMMAND='echo -ne "\033]0;${USER}#${HOSTNAME%%.*} $(eval "echo ${MYPSDIR}")\007"'
else
export PROMPT_COMMAND=''
fi
It checks whether you are not inside emacs, and only then the PROMPT_COMMAND variable is set to your desired value.
The extra display is coming from the PROMPT_COMMAND variable's contents. emacs appears not to understand the OSC 0 title setting xterm escape sequence and so prints out the output.

ZSH script and prompt profiling?

This answer, "How to profile a bash shell script?", seems to nearly perfectly cover what I'm trying to accomplish here. I currently have some zsh scripts that modify the prompt, however I think some updates to oh-my-zsh have evoked some issues that I need to hunt down. The sluggishness from time to time is unbearable.
To this end, how would you adapt the prompt sections in this example answer to work with zsh vs bash?
Presently I have modified /etc/zshenv such that it has the initial suggested code from the example:
PS4='+ $(date "+%s.%N")\011 '
exec 3>&2 2>/tmp/bashstart.$$.log
set -x
And my ~/.zshrc has the following appended to it's tail:
set +x
exec 2>&3 3>&-
Of course these are not valid for ZSH shell customization. My prompt rendering code utilizes oh-my-zsh customizations. I could prepend the appropriate code to the prompt I suppose or I'm open to other suggestions.
Calling date for each command will fork and exec, which adds overhead which may interfere with your measurements.
Instead, you could use
PS4=$'+ %D{%s.%6.}\011 '
to log timestamps with lower overhead (up to millisecond precision).
For some notes on processing the resulting logs, see http://blog.xebia.com/profiling-zsh-shell-scripts/
You may need to do
setopt prompt_subst
if it's not already.
Also, in order to interpret the octal escape for tab, use $'':
PS4=$'+ $(date "+%s.%N")\011 '
You may also find some of these escapes to be useful:
%? The return status of the last command executed just before the prompt.
%_ The status of the parser, i.e. the shell constructs (like `if' and `for') that have been started on the command
line. If given an integer number that many strings will be printed; zero or negative or no integer means print as
many as there are. This is most useful in prompts PS2 for continuation lines and PS4 for debugging with the
XTRACE option; in the latter case it will also work non-interactively.
%i The line number currently being executed in the script, sourced file, or shell function given by %N. This is most
useful for debugging as part of $PS4.
%I The line number currently being executed in the file %x. This is similar to %i, but the line number is always a
line number in the file where the code was defined, even if the code is a shell function.
%L The current value of $SHLVL.
%N The name of the script, sourced file, or shell function that zsh is currently executing, whichever was started
most recently. If there is none, this is equivalent to the parameter $0. An integer may follow the `%' to spec‐
ify a number of trailing path components to show; zero means the full path. A negative integer specifies leading
components.
%x The name of the file containing the source code currently being executed. This behaves as %N except that function
and eval command names are not shown, instead the file where they were defined.

What is the difference between PS1 and PROMPT_COMMAND?

While taking a look at this awesome thread I noticed that some examples use
PS1="Blah Blah Blah"
and some use
PROMPT_COMMAND="Blah Blah Blah"
(and some use both) when setting the prompt in a Bash shell. What is the difference between the two? A Stack Overflow search and even a bit of broader Google searching aren't getting me results, so even a link to the right place to look for the answer would be appreciated.
PROMPT_COMMAND can contain ordinary Bash statements whereas the PS1 variable can also contain the special characters, such as '\h' for hostname, in the variable.
For example, here is my Bash prompt that uses both PROMPT_COMMAND and PS1. The Bash code in PROMPT_COMMAND works out what Git branch you might be in and displays that at the prompt, along with the exit status of the last run process, hostname and basename of the pwd.
The variable RET stores the return value of the last executed program. This is convenient to see if there was an error and the error code of the last program I ran in the terminal. Note the outer ' surrounding the entire PROMPT_COMMAND expression. It includes PS1 so that this variable is reevaluated each time the PROMPT_COMMAND variable is evaluated.
PROMPT_COMMAND='RET=$?;\
BRANCH="";\
ERRMSG="";\
if [[ $RET != 0 ]]; then\
ERRMSG=" $RET";\
fi;\
if git branch &>/dev/null; then\
BRANCH=$(git branch 2>/dev/null | grep \* | cut -d " " -f 2);\
fi;
PS1="$GREEN\u#\h $BLUE\W $CYAN$BRANCH$RED$ERRMSG \$ $LIGHT_GRAY";'
Example output looks like this in a non-Git directory:
sashan#dhcp-au-122 Documents $ false
sashan#dhcp-au-122 Documents 1 $
And in a Git directory you see the branch name:
sashan#dhcp-au-122 rework mybranch $
Update
After reading the comments and Bob's answer, I think that writing it as he describes is better. It's more maintainable than what I originally wrote above, where the PS1 variable is set inside the PROMPT_COMMAND, which itself is a super complicated string that is evaluated at runtime by Bash.
It works, but it's more complicated than it needs to be. To be fair, I wrote that PROMPT_COMMAND for myself about 10 years ago and it worked and didn't think too much about it.
For those curious as to how I've amended my things, I've basically put the code for the PROMPT_COMMAND in a separate file (as Bob described) and then echo the string that I intend to be PS1:
GREEN="\[\033[0;32m\]"
CYAN="\[\033[0;36m\]"
RED="\[\033[0;31m\]"
PURPLE="\[\033[0;35m\]"
BROWN="\[\033[0;33m\]"
LIGHT_GRAY="\[\033[0;37m\]"
LIGHT_BLUE="\[\033[1;34m\]"
LIGHT_GREEN="\[\033[1;32m\]"
LIGHT_CYAN="\[\033[1;36m\]"
LIGHT_RED="\[\033[1;31m\]"
LIGHT_PURPLE="\[\033[1;35m\]"
YELLOW="\[\033[1;33m\]"
WHITE="\[\033[1;37m\]"
RESTORE="\[\033[0m\]" #0m restores to the terminal's default colour
if [ -z $SCHROOT_CHROOT_NAME ]; then
SCHROOT_CHROOT_NAME=" "
fi
BRANCH=""
ERRMSG=""
RET=$1
if [[ $RET != 0 ]]; then
ERRMSG=" $RET"
fi
if which git &>/dev/null; then
BRANCH=$(git branch 2>/dev/null | grep \* | cut -d " " -f 2)
else
BRANCH="(git not installed)"
fi
echo "${GREEN}\u#\h${SCHROOT_CHROOT_NAME}${BLUE}\w \
${CYAN}${BRANCH}${RED}${ERRMSG} \$ $RESTORE"
And in my .bashrc file:
function prompt_command {
RET=$?
export PS1=$(~/.bash_prompt_command $RET)
}
PROMPT_DIRTRIM=3
export PROMPT_COMMAND=prompt_command
From the GNU Bash documentation page (Bash Reference Manual):
PROMPT_COMMAND
If set, the value is interpreted as a command to execute before
the printing of each primary prompt ($PS1).
I never used it, but I could have used this back when I only had sh.
The difference is that PS1 is the actual prompt string used, and PROMPT_COMMAND is a command that is executed just before the prompt. If you want the simplest, most flexible way of building a prompt, try this:
Put this in your .bashrc file:
function prompt_command {
export PS1=$(~/bin/bash_prompt)
}
export PROMPT_COMMAND=prompt_command
Then write a script (Bash, Perl, or Ruby: your choice), and place it in file ~/bin/bash_prompt.
The script can use any information it likes to construct a prompt. This is much simpler, IMO, because you don't have to learn the somewhat baroque substitution language that was developed just for the PS1 variable.
You might think that you could do the same by simply setting PROMPT_COMMAND directly to ~/bin/bash_prompt, and setting PS1 to the empty string.
This at first appears to work, but you soon discover that the readline code expects PS1 to be set to the actual prompt, and when you scroll backwards in history, things get messed up as a result.
This workaround causes PS1 to always reflect the latest prompt (since the function sets the actual PS1 variable used by the invoking instance of the shell), and this makes readline and command history work fine.
From man bash:
PROMPT_COMMAND
If set, the value is executed as a command prior to issuing each primary prompt.
PS1
The value of this parameter is expanded (see PROMPTING below) and used as the primary prompt string. The default value is ''\s-\v\$ ''.
If you simply want to set the prompt string, using PS1 alone is enough:
PS1='user \u on host \h$ '
If you want to do something else just before printing the prompt, use PROMPT_COMMAND. For example, if you want to sync cached writes to disk, you can write:
PROMPT_COMMAND='sync'
Yeah, so to try to really nail this down:
PROMPT_COMMAND is a handy Bash convenience variable/function, but there is, strictly speaking, nothing that cannot also be done using PS1 alone, correct?
I mean, if one wants to set another variable with scope outside the prompt: depending on the shell, that variable would probably need to be declared first outside $PS1 or (worst case) one might have to get fancy with something waiting on a FIFO prior to calling $PS1 (and armed again at the end of $PS1); the \u \h might cause some trouble, particularly if you're using some fancy regex; but otherwise: one can accomplish anything PROMPT_COMMAND can by using command substitution within $PS1 (and, maybe in corner cases, explicit subshells)?
Right?
The difference is that
if you output an incomplete line from PROMPT_COMMAND, it will screw your Bash prompt
PS1 substitutes \H and friends
PROMPT_COMMAND runs its contents, and PS1 uses its contents as the prompt.
PS1 does variable expansion and command substitution at each prompt. There isn't any need to use PROMPT_COMMAND to assign a value to PS1 or to run arbitrary code. You can easily do export PS1='$(uuidgen) $RANDOM' once in file .bash_profile. Just use single quotes.
I spent so much time on this I just wanted to share what worked for me. I looked at a lot of the SO posts about PROMPT_COMMAND and PS1 and tried many combinations of single quotes, double quotes, calling functions... I could not get the prompt to update each time without printing control characters or the literal expanded but not processed prompt string, or without just setting PS1 in PROMPT_COMMAND as we are advised not to do. My problem was setting variables (colors) that contained control characters; these had to be hard-coded after the variable name in PS1. PROMPT_COMMAND is set to a function that sets variables and they are used (escaped) in a double-quoted PS1 string. This is for a powerline-style prompt that changes colors with each command.
icon1=#unicode powerline char like
#these: https://github.com/ryanoasis/powerline-extra-symbols#glyphs
icon2=#same
#array of ANSI colors. 2 for rgb mode then the rgb values
#then 'm' without '\]' control character. these are from
#the solarized theme https://ethanschoonover.com/solarized/
declare -a colors=(
"2;220;50;47m"
"2;203;75;22m"
"2;181;137;0m"
"2;133;153;0m"
"2;42;161;152m"
"2;38;139;210m"
"2;108;113;196m"
"2;211;54;130m"
"2;0;43;54m"
"2;7;54;66m"
"2;88;110;117m"
"2;101;123;131m"
"2;131;148;150m"
"2;147;161;161m"
)
#outside of vars set in PROMPT_COMMAND it's ok to have control chars
LEN=${#colors[#]}
BG="\[\e[48;"#set bg color
FG="\[\e[38;"#set fg color
TRANSP="1m\]"#transparency
BASE2="2;238;232;213m\]"#fg (text) color
myfunc(){
RAND=$(($RANDOM % $LEN))
COLOR1=${colors[$RAND]}
COLOR2=${colors[($RAND + 1) % $LEN]}
COLOR3=${colors[($RAND + 2) % $LEN]}
}
PROMPT_COMMAND=myfunc
#note double quotes and escaped COLOR vars followed by hard-coded '\]' control chars
PS1="$BG$TRANSP$FG\$COLOR1\]$icon1$BG\$COLOR1\]$FG$TRANSP$BG\$COLOR1\]$FG$BASE2
[username hard-coded in unicode] $BG\$COLOR2\]$FG\$COLOR1\]$icon2$BG\$COLOR2\]$FG$BASE2
\w $BG\$COLOR3\]$FG\$COLOR2\]$icon2$BG\$COLOR3\]$FG$BASE2 [more unicode]
\[\e[0m\]$FG\$COLOR3\]$icon2\[\e[0m\] "
That ought to get you going!

Resources