I have a fancy prompt with colors, and now bash doesn't seem to know how wide my terminal is. Lines wrap around incorrectly.
I have another proper way to do this, put this code in your ~/.bashrc
or create a new file and source file
:
PROMPT_COMMAND=$(
cat<<-'EOF'
retval=$?
RED=$(tput setaf 1)
GREEN=$(tput setaf 2)
STOP=$(tput sgr0)
# arithmetic using bash parameter expansion on a array
if (($retval + ${PIPESTATUS[@]/%/+} + 0)); then
PS1="\[$RED\]\u@\h:\w$ \[$STOP\]"
else
PS1="\[$GREEN\]\u@\h:\w$ \[$STOP\]"
fi
EOF
)
That will do the trick =)
Bash will run the code inside PROMPT_COMMAND
for each commands.
If you have copy/paste problem, you can download the script
EXPLANATIONS
(( ))
is arithmetic in bash
, see http://wiki.bash-hackers.org/syntax/arith_expr
PROMPT_COMMAND
: if set, the value is executed as a command prior to issuing each primary prompt. See man bash | less +/PROMPT_COMMAND
tput
is better than hard coding ANSI escape codes. See http://wiki.bash-hackers.org/scripting/terminalcodes
PIPESTATUS
: An array variable containing a list of exit status values from the processes in the most-recently-executed foreground pipeline (which may contain only a single command). See man bash | less +/PIPESTATUS
cat<<-'EOF'
is a special here doc : the -
character means I can indent code, and the single quotes on 'EOF'
means to not interpolate variables
sudo
can be configured to set $HOME
to match the new user when switching users, or not to. In your case, it appears it is set to not switch $HOME
. That can be useful when you want to switch to user accounts that are dedicated to specific service applications, but still want to keep using your personal shell configuration and other settings.
And ~/.bashrc
is actually just a shorthand for $HOME/.bashrc
.
So when you do sudo -u newuser bash
, sudo
switches the username to newuser
but $HOME
is still set to /home/myuser
.
When bash
attempts to execute ~/.bashrc
, it has a slight problem: as newuser
, it might have no access to read /home/myuser/.bashrc
unless you have specifically granted that access. If you haven't, that is probably why .bashrc
is getting skipped. Because $HOME
is still set to /home/myuser
, there will be no attempt to execute /home/newuser/.bashrc
.
If you want sudo
to set HOME=/home/newuser
, you can:
- use the
-H
option with the sudo command: sudo -Hu newuser bash
- or add
Defaults>newuser always_set_home
to your sudoers
file to automatically set the home directory when switching to newuser
only
- or add
Defaults:myuser always_set_home
to sudoers file to automatically set the home directory to match the new identity when myuser
is running any sudo
commands
- or add
Defaults always_set_home
to sudoers file to force this behavior for all sudo
commands on the system. (Some Linux distributions have this enabled by default.)
If you don't especially want to switch the home directory when switching users, then you'll need to make sure the new user can read your personal shell startup files instead.
The minimal permissions you'd need to allow for sudo -u newuser bash
to run /home/myuser/.bashrc
are:
- a search permission to
/home/myuser
- a read permission to
/home/myuser/.bashrc
If there is no convenient user group that would be common to both myuser
and newuser
, and ACLs are not available (i.e. traditional Unix-style permissions only), this would be the traditional way to do it:
chmod 711 /home/myuser # i.e. directory permissions drwx--x--x
chmod 644 /home/myuser/.bashrc # i.e. file permissions -rw-r--r--
If ACLs are available for the filesystem containing your home directory, you could do this instead to grant the minimal necessary access:
setfacl -m u:newuser:x /home/myuser
setfacl -m u:newuser:r /home/myuser/.bashrc
If the permissions of the home directory were initially drwx------
, after this command the permissions to /home/myuser
would look like drwx--x---+
. To check the complete ACL, use getfacl
:
$ getfacl /home/myuser
getfacl: Removing leading '/' from absolute path names
# file: home/myuser
# owner: myuser
# group: myuser
user::rwx
user:newuser:--x
group::---
mask::--x
other::---
Best Answer
Your assignments to
PROMPT_LAST_EXIT_STATUS
aren't being quoted, so you're not putting\[
and\]
into the string, you're just putting[
and]
(because the\
s are being treated as escape characters).Compare:
Vs:
Not only that: parameter expansion (the interpolation of variables into the prompt string) happens after the the expansion of prompt special characters. So, putting the
\[
and\]
into thePROMPT_LAST_EXIT_STATUS
variable won't work, as by the time$PROMPT_LAST_EXIT_STATUS
is expanded, the\[
and\]
are no longer special. A working alternative would move the color setting to be unconditional, something like: