Syntax error: end of file unexpected (expecting "done") [closed] - linux

Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 2 years ago.
Improve this question
I copied a shell script of SeisUnix(SU), a Linux based program for seismic processing.
#! /bin/sh
#File: Nmigcvp2.sh
# Create one panel for each migration velocity
# Each panel has the same "fldr" value
# The migration velocity is in key "offset"
# Total number of panels is in key "nvs"
# Set messages on
##set -x
#================================================
# USER AREA -- SUPPLY VALUES
#------------------------------------------------
# Seismic files
indata=Nstack4.su # SU format
outdata=Nmigcvp.su # migration Constant Velocity Panels
# Migration variables
cdpmin=900 # Start CDP value
cdpmax=1300 # End CDP value
dxcdp=16.667 # distance between adjacent CDP bins (m)
smig=1.0 # stretch factor (0.6 typical if vrms increasing)
# [the "W" factor] (Default=1.0)
vscale=1.0 # scale factor to apply to velocities (Default=1.0)
lstaper=20 # length of side tapers (traces) (Default=0)
lbtaper=100 # length of bottom taper (samples) (Default=0)
# Velocity panel variables
firstv=1400 # first velocity value
lastv=2000 # last velocity value
increment=200 # velocity increment
numVtest=100 # use to limit number of velocity panels
# otherwise, use very large value (100)
#================================================
# Compute number of velocity panels
numV=`echo "( ( $lastv - $firstv ) / $increment ) + 1" |bc -l
if [ $numVtest -lt $numV ] ; then
numV=$numVtest
fi
#------------------------------------------------
# FILE DESCRIPTIONS
# tmp1 = binary temp file of input data
#------------------------------------------------
cp $indata tmp1
migV=$firstv
echo " "
#------------------------------------------------
# Loop through Migration Constant Velocity Panels
# Each panel has the same "fldr" value
# Panel migration velocity is in key "offset"
# Total number of panels (numV) is in key "nvs"
#------------------------------------------------
i=1
while [ $i -le $numV ]
do
echo " iteration number = $i Velocity = $migV"
suwind < tmp1 key=cdp min=$cdpmin max=$cdpmax |
sushw key=fldr a=$i |
sushw key=offset a=$migV |
sushw key=nvs a=$numV |
sustolt cdpmin=$cdpmin cdpmax=$cdpmax dxcdp=$dxcdp \
tmig=0 vmig=$migV smig=$smig vscale=$vscale \
lstaper=$lstaper lbtaper=$lbtaper \
>> $outdata
i=`expr $i + 1`
migV=`expr $migV + $increment`
done
#------------------------------------------------
# Remove files and exit
#------------------------------------------------
echo " "
echo " Output file = $outdata"
echo " "
rm -f tmp*
exit
When I run the script I get a syntax error.
./Nmigcvp2.sh: 33: Syntax error: end of file unexpected (expecting "done")

numV=`echo "( ( $lastv - $firstv ) / $increment ) + 1" |bc -l`
You're missing a ` backtick char at end of line here. If you use a colored syntax editor it is easier to find this kind of syntax error.
In VIM for example use set syntax & colorscheme elflord.
You can also numerate lines set nu.

Related

Basic Quadratic formula calculator shell script trouble

This is my very first shell script for a Unix class, this is one of the scripts I hope to submit for my final. However there are a few kinks I cannot seem to clear up, it seems to be arithmetic operation errors, and I can't seem to figure it out. Please be kind! thank you so much for your time.
lightgreen=`echo -en "\e[92m"
echo What are the values of a, b \& c?
LIGHTRED=`echo -en "\e[101m"
echo a value:
read a
echo b value:
read b
echo c value:
read c
discrim=$(($b**2 - 4*$a*$c))
sqrtd=$((sqrt($discrim) | bc ))
echo test $sqrtd
echo ${lightgreen}The discriminant is:${discrim}
#xone=$((( -$b + sqrt$discrim) / (2 * $a) | bc ))
#xtwo=$((( -$b - sqrt$discrim) / (2 * $a) | bc ))
xone=$((echo (-1*$b + sqrt($discrim)) / (2*$a) | bc ))
xtwo=$((echo (-1*$b - sqrt($discrim)) / (2*$a) | bc ))
echo ${lightgreen}The discriminant is:${discrim}
#if [$discrim -lt 0 ]
# echo $LIGHTRED There are no real solutions.
#
#
#
echo The two solutions are $xone $xtwo
I have tried to mess with the syntax a good amount, I'm not sure if it's the parentheses that mess me up or the sqrt function, I have tried to incorporate | bc but to no avail. Any help is greatly appreciated! :)
Don't hesitate to call man bash, man bc manual pages.
Use https://www.shellcheck.net/ to check your shell scripts.
Shellcheck also exists on command line and in Visual Studio Code with extension.
#! /usr/bin/env bash
# The first line is very important to now the name of the interpreter
# Always close " , ' , or ` sequences with same character
# Do not use old `...` syntax, replaced by $(...)
# Here, use $'...', to assign value with \... sequences
lightgreen=$'\e[92m'
lightred=$'\e[101m'
normal=$'\e[0m'
# It's better to put phrase between "..." or '...'
echo "What are the values of a, b & c?"
# Use read -p option to specify prompt
# Use read -r option to not act backslash as an escape character
read -p "a value: " -r a
read -p "b value: " -r b
read -p "c value: " -r c
# With bash only, it's only possible to use integer values
# discrim=$(($b**2 - 4*$a*$c))
# use bc instead
discrim=$(bc -l <<<"$b^2 - 4*$a*$c")
# The syntax:
# bc <<<"..."
# is equivalent to:
# echo "..." | bc
# but without pipe (|)
# Close the color change with normal return
echo "${lightgreen}The discriminant is: ${discrim}${normal}"
if [[ "${discrim:0:1}" == "-" ]]; then
echo "${lightred}There are no real solutions${normal}"
# ... complex ...
else
sqrtd=$(bc -l <<<"sqrt($discrim)")
echo "sqrt($discrim)=$sqrtd"
xone=$(bc -l <<<"(-1*$b + $sqrtd) / (2*$a)")
xtwo=$(bc -l <<<"(-1*$b - $sqrtd) / (2*$a)")
echo "The two solutions are: $xone and $xtwo"
fi

Gnuplot 4d map colorbar with different transparency based on data

I am trying to plot 4d plot using splot (x y z value). I would like to have 4th column shown as heat color. Up to this point, I am fine. What I can't figure out, after searching answers online, is to have the color of the dots being transparent but with different transparency based on their value.
For example, let's say I have the following data set:
0 0 0 0.1
0 0 1 0.2
0 1 0 0.2
0 1 1 2
1 0 0 1
1 0 1 3
1 1 0 0.5
1 1 1 4
Now, I want to make the colorbar (for the 4th column) to be as such: the closer the 4th column value is to 1, the more transparent the dot/point in the figure it will be. All the places I've look can only give me uniform transparency for the entire colorbar.
I wonder if anyone has dealt with this before, or has an idea how to do this.
Thank you very much!
I did not fully understand how you want the transparency to depend on the value, so I will give a general answer, where you can substitute the transparency function for your own.
While you can specify transparency for line colors, this appears to not be possible when using palette, which would be the most straightforward way to achieve what you want. The furthest you can go with only using gnuplot is to make the colors appear transparent, as in the following script, where in.data is a file with your sample data.
#!/usr/bin/env gnuplot
set term pngcairo
in_data = "in.data"
set out "out.png"
# function to combine color and alpha channels with white background
# 0: no transparency, 1: fully transparent
make_transparent(x1, t) = (1-t)*x1 + t
# a function to decide transparency
# the input and output must be in range of [0,1]
#get_transparency(x1) = 0 # no transparency
#get_transparency(x1) = 1 # fully transparent
get_transparency(x1) = 1 - x1 # smaller values are more transparent
# convenience function to truncate values
minval(x1, x2) = x1<x2?x1:x2
maxval(x1, x2) = x1>x2?x1:x2
truncval(x1, xmin, xmax) = maxval(minval(x1, xmax), xmin)
trunc(x1) = truncval(x1, 0, 1)
# the default palette consists of rgbfunctions 7,5,15
# we redefine their transparency enabled versions here
# the input and output must be in range of [0,1]
# see other formulae with "show palette rgbformulae" command in gnuplot
f7(x1) = make_transparent(sqrt(x1) , get_transparency(x1))
f5(x1) = make_transparent(x1**3 , get_transparency(x1))
f15(x1) = make_transparent(trunc(sin(2*pi*x1)), get_transparency(x1))
set palette model RGB functions f7(gray),f5(gray),f15(gray)
splot in_data palette
This script assumes that the background is white, but can be adapted to any other solid background. It falls apart once the points start overlapping however.
To get real transparency, you would need to plot each data point as a separate line and give it a distinct line color. This could be achieved by pre-processing the data, as in the following bash script.
#!/usr/bin/env bash
set -eu
in_data="in.data"
out_png="out.png"
pi=3.141592653589793
# function to convert data value into rgba value
function value2rgba()
{
# arguments to function
local val="${1}"
local min="${2}"
local max="${3}"
# normalized value
local nval=$( bc -l <<< "(${val}-${min})/(${max}-${min})" )
#### alpha channel value ####
local alpha="$( bc -l <<< "255 * (1-${nval})" )"
# round to decimal
alpha=$( printf "%0.f" "${alpha}" )
#### red channel value ####
# rgbformulae 7 in gnuplot
local red="$( bc -l <<< "255 * sqrt(${nval})" )"
# round to decimal
red=$( printf "%0.f" "${red}" )
#### green channel value ####
# rgbformulae 5 in gnuplot
local red="$( bc -l <<< "255 * sqrt(${nval})" )"
local green="$( bc -l <<< "255 * ${nval}^3" )"
# round to decimal
green=$( printf "%0.f" "${green}" )
#### blue channel value ####
# rgbformulae 15 in gnuplot
local blue="$( bc -l <<< "255 * s(2*${pi}*${nval})" )"
# round to decimal
blue=$( printf "%0.f" "${blue}" )
# make sure blue is positive
if (( blue < 0 ))
then
blue=0
fi
### whole rgba value
local rgba="#"
rgba+="$( printf "%02x" "${alpha}" )"
rgba+="$( printf "%02x" "${red}" )"
rgba+="$( printf "%02x" "${green}" )"
rgba+="$( printf "%02x" "${blue}" )"
echo "${rgba}"
}
# data without blank lines
data="$( sed -E "/^[[:space:]]*$/d" "${in_data}" )"
# number of lines
nline=$( wc -l <<< "${data}" )
# get the minimum and maximum value of the 4-th column
min_max=( $( awk '{ print $4 }' <<< "${data}" | sort -g | sed -n '1p;$p' ) )
# array of colors for each point
colors=()
while read -r line
do
colors+=( $( value2rgba "${line}" "${min_max[#]}" ) )
done < <( awk '{ print $4 }' <<< "${data}" )
# gather coordinates into one row
coords=( $( awk '{ print $1,$2,$3 }' <<< "${data}" ) )
gnuplot << EOF
set term pngcairo
set out "${out_png}"
\$DATA << EOD
${coords[#]}
EOD
nline=${nline}
colors="${colors[#]}"
unset key
splot for [i=0:nline-1] \$DATA \
u (column(3*i+1)):(column(3*i+2)):(column(3*i+3)) \
pt 1 lc rgb word(colors, i+1)
EOF
These scripts were tested with gnuplot 5.

How to print something to the right-most of the console in Linux shell script

Say I want to search for "ERROR" within a bunch of log files.
I want to print one line for every file that contains "ERROR".
In each line, I want to print the log file path on the left-most edge while the number of "ERROR" on the right-most edge.
I tried using:
printf "%-50s %d" $filePath $errorNumber
...but it's not perfect, since the black console can vary greatly, and the file path sometimes can be quite long.
Just for the pleasure of the eyes, but I am simply incapable of doing so.
Can anyone help me to solve this problem?
Using bash and printf:
printf "%-$(( COLUMNS - ${#errorNumber} ))s%s" \
"$filePath" "$errorNumber"
How it works:
$COLUMNS is the shell's terminal width.
printf does left alignment by putting a - after the %. So printf "%-25s%s\n" foo bar prints "foo", then 22 spaces, then "bar".
bash uses the # as a parameter length variable prefix, so if x=foo, then ${#x} is 3.
Fancy version, suppose the two variables are longer than will fit in one column; if so print them on as many lines as are needed:
printf "%-$(( COLUMNS * ( 1 + ( ${#filePath} + ${#errorNumber} ) / COLUMNS ) \
- ${#errorNumber} ))s%s" "$filePath" "$errorNumber"
Generalized to a function. Syntax is printfLR foo bar, or printfLR < file:
printfLR() { if [ "$1" ] ; then echo "$#" ; else cat ; fi |
while read l r ; do
printf "%-$(( ( 1 + ( ${#l} + ${#r} ) / COLUMNS ) \
* COLUMNS - ${#r} ))s%s" "$l" "$r"
done ; }
Test with:
# command line args
printfLR foo bar
# stdin
fortune | tr -s ' \t' '\n\n' | paste - - | printfLR

multiple variable definition and assignments

I have a piece of script that basically calculates the amount of space the directories in the current directory use but I want help understanding some of the syntax and language etiquette.
Here is the script:
#!/bin/bash
# This script prints a little histogram of how much space
# the directories in the current working directory use
error () {
echo "Error: $1"
exit $2
} >&2
# Create a tempfile (in a BSD- and Linux-friendly way)
my_mktemp () {
mktemp || mktemp -t hist
} 2> /dev/null
# check we are using bash 4
(( BASH_VERSINFO[0] < 4 )) && error "This script can only be run by bash 4 or higher" 1
# An array to keep all the file sizes
declare -A file_sizes
declare -r tempfile=$(my_mktemp) || error "Cannot create tempfile" 2
# How wide is the terminal?
declare -ir term_cols=$(tput cols)
# Longest file name, Largest file, total file size
declare -i max_name_len=0 max_size=0 total_size=0
# A function to draw a line
drawline () {
declare line=""
declare char="-"
for (( i=0; i<$1; ++i )); do
line="${line}${char}"
done
printf "%s" "$line"
}
# This reads the output from du into an array
# And calculates total size and maximum size, max filename length
read_filesizes () {
while read -r size name; do
file_sizes["$name"]="$size"
(( total_size += size ))
(( max_size < size )) && (( max_size=size ))
(( max_file_len < ${#name} )) && (( max_file_len=${#name} ))
done
}
# run du to get filesizes
# Using a temporary file for output from du
{ du -d 0 */ || du --max-depth 0 *; } 2>/dev/null > "$tempfile"
read_filesizes < "$tempfile"
# The length for each line and percentage for each file
declare -i length percentage
# How many columns may the lines take up?
declare -i cols="term_cols - max_file_len - 10"
for k in "${!file_sizes[#]}"; do
(( length=cols * file_sizes[$k] / max_size ))
(( percentage=100 * file_sizes[$k] / total_size ))
printf "%-${max_file_len}s | %3d%% | %s\n" "$k" "$percentage" $(drawline $length)
done
printf "%d Directories\n" "${#file_sizes[#]}"
printf "Total size: %d blocks\n" "$total_size"
# clean up
rm "$tempfile"
exit 0
In the first and second line of the read_filesizes() function that I highlighted in bold, why are two variables (size name) being created if the name is being assigned to size in the array?
In the same function, (( max_size < size )) && (( max_size=size )) this line seems odd to me because how can the two expressions both be true?
Then in the first line of the for loop, (( **length=cols** * file_sizes[$k] / max_size )) I don't understand why the variable length is assigned to cols..why were they defined separately to begin with?
While I'm not 100% sure of the syntax, it seems clear enough to answer your questions :
First Question
why are two variables (size name) being created if the name is being assigned to size in the array?
It looks like name holds the file name and size holds the file size. Then the assignment file_sizes["$name"]="$size" stores the file sizes indexed by the file names.
Second Question
(( max_size < size )) && (( max_size=size ))
I believe this line assigns size to max_size if the previous value of max_size is smaller than size. The goal is that at the end max_size would hold the size of the largest file.
Third Question
(( length=cols * file_sizes[$k] / max_size ))
This calculates the length of the line that would be displayed for each file (whose goal is probably to illustrate the relative size of the file compared to the largest file). The length of the line is relative to the size of the file. cols is the length of the line that would be displayed for the largest file (the one whose size is max_size). cols = the lengh of the terminal - the length of the longest file name - 10.

Bash prompt with the last exit code

I've been trying to customize my Bash prompt so that it will look like
[feralin#localhost ~]$ _
with colors. I managed to get constant colors (the same colors every time I see the prompt), but I want the username ('feralin') to appear red, instead of green, if the last command had a nonzero exit status. I came up with:
\e[1;33m[$(if [[ $? == 0 ]]; then echo "\e[0;31m"; else echo "\e[0;32m"; fi)\u\e[m#\e[1;34m\h \e[0;35m\W\e[1;33m]$ \e[m
However, from my observations, the $(if ...; fi) seems to be evaluated once, when the .bashrc is run, and the result is substituted forever after. This makes the name always green, even if the last exit code is nonzero (as in, echo $?). Is this what is happening? Or is it simply something else wrong with my prompt? Long question short, how do I get my prompt to use the last exit code?
As you are starting to border on a complex PS1, you might consider using PROMPT_COMMAND. With this, you set it to a function, and it will be run after each command to generate the prompt.
You could try the following in your ~/.bashrc file:
PROMPT_COMMAND=__prompt_command # Function to generate PS1 after CMDs
__prompt_command() {
local EXIT="$?" # This needs to be first
PS1=""
local RCol='\[\e[0m\]'
local Red='\[\e[0;31m\]'
local Gre='\[\e[0;32m\]'
local BYel='\[\e[1;33m\]'
local BBlu='\[\e[1;34m\]'
local Pur='\[\e[0;35m\]'
if [ $EXIT != 0 ]; then
PS1+="${Red}\u${RCol}" # Add red if exit code non 0
else
PS1+="${Gre}\u${RCol}"
fi
PS1+="${RCol}#${BBlu}\h ${Pur}\W${BYel}$ ${RCol}"
}
This should do what it sounds like you want. Take a look a my bashrc's sub file if you want to see all the things I do with my __prompt_command function.
If you don't want to use the prompt command there are two things you need to take into account:
getting the value of $? before anything else. Otherwise it'll be overridden.
escaping all the $'s in the PS1 (so it's not evaluated when you assign it)
Working example using a variable
PS1="\$(VALU="\$?" ; echo \$VALU ; date ; if [ \$VALU == 0 ]; then echo zero; else echo nonzero; fi) "
Working example without a variable
Here the if needs to be the first thing, before any command that would override the $?.
PS1="\$(if [ \$? == 0 ]; then echo zero; else echo nonzero; fi) "
Notice how the \$() is escaped so it's not executed right away, but each time PS1 is used. Also all the uses of \$?.
Compact solution:
PS1='... $(code=${?##0};echo ${code:+[error: ${code}]})'
This approach does not require PROMPT_COMMAND (apparently this can be slower sometimes) and prints [error: <code>] if the exit code is non-zero, and nothing if it's zero:
... > false
... [error: 1]> true
... >
Change the [error: ${code}] part depending on your liking, with ${code} being the non-zero code to print.
Note the use of ' to ensure the inline $() shell gets executed when PS1 is evaluated later, not when the shell is started.
As bonus, you can make it colorful in red by adding \e[01;31m in front and \e[00m after to reset:
PS1='... \e[01;31m$(code=${?##0};echo ${code:+[error: ${code}]})\e[00m'
--
How it works:
it uses bash parameter substitution
first, the ${?##0} will read the exit code $? of the previous command
the ## will remove any 0 pattern from the beginning, effectively making a 0 result an empty var (thanks #blaskovicz for the trick)
we assign this to a temporary code variable as we need to do another substitution, and they can't be nested
the ${code:+REPLACEMENT} will print the REPLACEMENT part only if the variable code is set (non-empty)
this way we can add some text and brackets around it, and reference the variable again inline: [error: ${code}]
I wanted to keep default Debian colors, print the exact code, and only print it on failure:
# Show exit status on failure.
PROMPT_COMMAND=__prompt_command
__prompt_command() {
local curr_exit="$?"
local BRed='\[\e[0;91m\]'
local RCol='\[\e[0m\]'
PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u#\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '
if [ "$curr_exit" != 0 ]; then
PS1="[${BRed}$curr_exit${RCol}]$PS1"
fi
}
The following provides a leading green check mark when the exit code is zero and a red cross in all other cases. The remainder is a standard colorized prompt. The printf statements can be modified to present the two states that were originally requested.
PS1='$(if [ $? -eq 0 ]; then printf "\033[01;32m""\xE2\x9C\x93"; else printf "\033[01;31m""\xE2\x9C\x95"; fi) \[\e[00;32m\]\u#\h\[\e[00;30m\]:\[\e[01;33m\]\w\[\e[01;37m\]\$ '
Why didn't I think about that myself? I found this very interesting and added this feature to my 'info-bar' project. Eyes will turn red if the last command failed.
#!/bin/bash
eyes=(O o ∘ ◦ ⍤ ⍥) en=${#eyes[#]} mouth='_'
face () { # gen random face
[[ $error -gt 0 ]] && ecolor=$RED || ecolor=$YLW
if [[ $1 ]]; then printf "${eyes[$[RANDOM%en]]}$mouth${eyes[$[RANDOM%en]]}"
else printf "$ecolor${eyes[$[RANDOM%en]]}$YLW$mouth$ecolor${eyes[$[RANDOM%en]]}$DEF"
fi
}
info () { error=$?
[[ -d .git ]] && { # If in git project folder add git status to info bar output
git_clr=('GIT' $(git -c color.ui=always status -sb)) # Colored output 4 info
git_tst=('GIT' $(git status -sb)) # Simple output 4 test
}
printf -v line "%${COLUMNS}s" # Set border length
date=$(printf "%(%a %d %b %T)T") # Date & time 4 test
test=" O_o $PWD ${git_tst[*]} $date o_O " # Test string
step=$[$COLUMNS-${#test}]; [[ $step -lt 0 ]] && step=0 # Count spaces
line="$GRN${line// /-}$DEF\n" # Create lines
home="$BLD$BLU$PWD$DEF" # Home dir info
date="$DIM$date$DEF" # Colored date & time
#------+-----+-------+--------+-------------+-----+-------+--------+
# Line | O_o |homedir| Spaces | Git status | Date| o_O | Line |
#------+-----+-------+--------+-------------+-----+-------+--------+
printf "$line $(face) $home %${step}s ${git_clr[*]} $date $(face) \n$line" # Final info string
}
PS1='${debian_chroot:+($debian_chroot)}\n$(info)\n$ '
case "$TERM" in xterm*|rxvt*)
PS1="\[\e]0;${debian_chroot:+($debian_chroot)} $(face 1) \w\a\]$PS1";;
esac
Improved demure answer:
I think this is important because the exit status is not always 0 or 1.
if [ $EXIT != 0 ]; then
PS1+="${Red}${EXIT}:\u${RCol}" # Add red if exit code != 0
else
PS1+="${Gre}${EXIT}:\u${RCol}" # Also displays exit status
fi
To preserve the original prompt format (not just colors),
you could append following to the end of file ~/.bashrc:
PS1_ORIG=$PS1 # original primary prompt value
PROMPT_COMMAND=__update_prompt # Function to be re-evaluated after each command is executed
__update_prompt() {
local PREVIOUS_EXIT_CODE="$?"
if [ $PREVIOUS_EXIT_CODE != 0 ]; then
local RedCol='\[\e[0;31m\]'
local ResetCol='\[\e[0m\]'
local replacement="${RedCol}\u${ResetCol}"
# Replace username color
PS1=${PS1_ORIG//]\\u/]$replacement}
## Alternative: keep same colors, append exit code
#PS1="$PS1_ORIG[${RedCol}error=$PREVIOUS_EXIT_CODE${ResetCol}]$ "
else
PS1=$PS1_ORIG
fi
}
See also the comment about the alternative approach that preserves username color and just appends an error code in red to the end of the original prompt format.
You can achieve a similar result to include a colored (non-zero) exit code in a prompt, without using subshells in the prompt nor prompt_command.
You color the exit code portion of the prompt, while having it only appear when non-zero.
Core 2$ section of the prompt: \\[\\033[0;31;4m\\]\${?#0}\\[\\033[0;33m\\]\$ \\[\\033[0m\\]
Key elements:
return code, if not 0: \${?#0} (specificly "removes prefix of 0")
change color without adding to calculated prompt-width: \\[\\033[0;31m\\]
\\[ - begin block
\\033 - treat as 0-width, in readline calculations for cmdline editing
[0;31;4m - escape code, change color, red fg, underline
\\] - end block
Components:
\\[\\033[0;31;4m\\] - set color 0;31m fg red, underline
\${?#0} - display non-zero status (by removing 0 prefix)
\\[\\033[0;33m\\] - set color 0;33m fg yellow
\$ - $ or # on EUID
\\[\\033[0m\\] - reset color
The full PS1 I use (on one host):
declare -x PS1="\\[\\033[0;35m\\]\\h\\[\\033[1;37m\\] \\[\\033[0;37m\\]\\w \\[\\033[0;33m\\]\\[\\033[0;31;4m\\]\${?#0}\\[\\033[0;33m\\]\$ \\[\\033[0m\\]"
Note: this addresses a natural extension to this question, in a more enduring way then a comment.
Bash
function my_prompt {
local retval=$?
local field1='\u#\h'
local field2='\w'
local field3='$([ $SHLVL -gt 1 ] && echo \ shlvl:$SHLVL)$([ \j -gt 0 ] && echo \ jobs:\j)'"$([ ${retval} -ne 0 ] && echo \ exit:$retval)"
local field4='\$'
PS1=$'\n'"\e[0;35m${field1}\e[m \e[0;34m${field2}\e[m\e[0;31m${field3}\e[m"$'\n'"\[\e[0;36m\]${field4}\[\e[m\] "
}
PROMPT_COMMAND="my_prompt; ${PROMPT_COMMAND}"
Zsh
PROMPT=$'\n''%F{magenta}%n#%m%f %F{blue}%~%f%F{red}%(2L. shlvl:%L.)%(1j. jobs:%j.)%(?.. exit:%?)%f'$'\n''%F{cyan}%(!.#.$)%f '
Images of prompt

Resources