I am wanting to colourize one word in the middle of an echo sentence, but can't seem to achieve this.
This works:
#!/bin/bash
wipe="\033[1m\033[0m"
yellow='\E[1;33'
echo -e "$yellow"
echo Hello World
echo -e "$wipe"
But this doesn't:
#!/bin/bash
wipe="\033[1m\033[0m"
yellow='\E[1;33'
black="40m"
echo -e "Output a $yellow coloured $wipe word."
# or
echo -e "Output a ${yellow} coloured ${wipe} word."
What am I stupidly doing wrong? :)
Much better, use tput to set a foreground colour:
textreset=$(tput sgr0) # reset the foreground colour
red=$(tput setaf 1)
yellow=$(tput setaf 2)
echo "Output a ${yellow} coloured ${textreset} ${red} word ${textreset}."
You forgot an m in your ANSI escape code for yellow. This works:
yellow='\E[1;33m'
Related
I want to have the same prompt in both Bash and Zsh. And I want it to:
bell a ring, if the last command failed, and
display its error code.
In Bash, I do have:
BLK="\[$(tput setaf 0; tput bold)\]"
RED="\[$(tput setaf 1; tput bold)\]"
grn="\[$(tput setaf 2)\]"
GRN="\[$(tput setaf 2; tput bold)\]"
yel="\[$(tput setaf 3)\]"
reset_color="\[$(tput sgr0)\]"
PS1='\n\
`if [[ $? -gt 0 ]]; then printf "\[\033[01;31m\]$?"; tput bel; else printf "\[\033[01;32m\]0"; fi`\
\[\033]0;$PWD\007\] \
\[\033[0;32m\]\u#\h\
\[\033[01;30m\]:\
\[\033[;;33m\]\w\
\[\033[36m\]`__git_ps1`\
\[\033[0m\]\n$ '
In Zsh, that's my config:
BLK=$(tput setaf 0; tput bold)
RED=$(tput setaf 1; tput bold)
grn=$(tput setaf 2)
GRN=$(tput setaf 2; tput bold)
yel=$(tput setaf 3)
reset_color=$(tput sgr0)
PROMPT="
%(?.$GRN.$RED)%?$reset_color $grn%n#%m$BLK:$reset_color$yel%~ $reset_color
%(!.#.$) "
And this is how it looks like in the terminal:
Both prompts do ring the bell when there is an error with the last command.
But, in Bash, it prints 0 instead of the right return code of the command that
failed.
How to fix that?
PS- Any better way to improve the above code is welcomed!
The command to test $? itself resets $? to the result of the test. You need to save the value you want to display first.
PS1='\n\
$(st=$?; if [[ $st -gt 0 ]]; then printf "\[\033[01;31m\]$st"; tput bel; else printf "\[\033[01;32m\]0"; fi)\
\[\033]0;$PWD\007\] \
\[\033[0;32m\]\u#\h\
\[\033[01;30m\]:\
\[\033[;;33m\]\w\
\[\033[36m\]`__git_ps1`\
\[\033[0m\]\n$ '
I would recommend building up the value of PS1 using PROMPT_COMMAND, instead of embedding executable code. This gives you more flexibility
for commenting and separating any computations you need from the actual
formatting. make_prompt doesn't need quite so many lines, but it's
just a demonstration.
set_title () {
printf '\033]0;%s%s' "$1" "$(tput bel)"
}
make_prompt () {
local st=$?
local c bell
bell=$(tput bel)
# Green for success, red and a bell for failure
if [[ $st -gt 0 ]]; then
c=31
else
c=32 bell=
fi
win_title=$(set_title "$PWD")
git_status=$(__git_ps1)
PS1="\n"
PS1+="\[\e[01;${c}m$bell\]" # exit status color and bell
PS1+=$st
PS1+="\[$win_title\]" # Set the title of the window
PS1+="\[\e[0;32m\]" # Color for user and host
PS1+="\u#\h"
PS1+="\[\e[01;30m\]" # Color for : separator
PS1+=":"
PS1+="\[\e[;;33m\]" # Color for directory
PS1+="\w"
PS1+="\[\e[36m\]" # Color for git branch
PS1+=$git_status
PS1+="\[\e[0m\]" # Reset to terminal defaults
PS1+="\n$ "
}
PROMPT_COMMAND=make_prompt
zsh already has terminal-agnostic escape sequences for adding color.
PROMPT="%B%(?.%F{green}.%F{red}$(tput bel))%?%f%b %F{green}%n#%m%F{black}%B:%b%F{yellow}%~ %f%(!.#.$) "
Any external command (such as printf or tput) resets the value of $?. You need to capture it in a variable before that happens.
rc=$?
if [ $rc -gt 0 ]; then
printf "\[\033[01;31m\]$rc"
tput bel
else
printf "\[\033[01;32m\]0"
fi
(Unwrapped for legibility; this will replace the code inside the backticks.)
Notice that this will still overwrite the value of $?; perhaps add exit $? at the end to properly preserve the value.
I've got a simple code setup, but for whatever reason it refuses to operate how it should
#!/bin/bash
RED='\033[0:31m'
RESET='\033[0m'
Basically these are some simple color encodings
Next thing I have is
for file in ./dir/*.c; do
echo "File [${RED}$file${RESET}] has been launched"
My expected result would be
File [myprogram.c] has been launched (while the name would be in red color)
Instead it just refuses to encode the color and dumps
File [\033[0:31m] has been launched
Any idea what I'm doing wrong?
First because red is '\033[31m'. What you wrote means something else, read below.
Then, because the codes need to be interpreted.
This won't work:
$ red='\033[31m'
$ echo "${red}Hello"
But this would:
$ red='\033[31m'
$ echo -e "${red}Hello"
Or, you can assign the interpreted values to a variable:
$ red="$(echo -e '\033[31m')"
$ red="$(printf '\033[31m')"
Then the escapes don't need to be interpreted:
$ echo "${red}Hello"
What you wrote (besides using a colon where a semicolon should be) was setting the "boldness" of the foreground, or 0 for thin/light and 1 for bold/bright.
$ printf '\033[31mHello\033[0;31mHello\033[1;31mHello'
Also, 3x is for foreground 4x is for background:
$ printf '\033[31;42mHello\033[0;44;31mHello\033[1;43;31mHello\033[0m'
A full table could be printed with this:
$ printf "$(printf '%s' 033[{0,1}';'3{1..8}{';'4{1..8}mXXX,';'40m=OoO\\033[0m\\n} )"
With tput
#!/bin/bash
red=$(tput setaf 1)
reset=$(tput sgr0)
for file in ./dir/*.c; do
echo "File [${red}$file${reset}] has been launched"
done
with printf
#!/bin/bash
RED='\033[0;31m'
RESET='\033[0m'
for file in ./dir/*.c; do
printf "File [${RED}$file${RESET}] has been launched"
done
There is a typo in your code. Red color code is [031m. The ASCII codes always start with an escape character, octal 33: \033. Thus, to start printing red text join the two sequences: \033[031m.
I find this post very helpful. In particular, the author recommends to use tput instead of the hard-coded values, and I agree with him.
Example
die() {
local message="$1"
: ${message:=Aborted}
# See info bash BASH_SOURCE, info bash FUNCNAME, info bash BASH_LINENO
printf '%s at %s:%s line %d\n' \
"$message" ${BASH_SOURCE[1]} ${FUNCNAME[1]} ${BASH_LINENO[0]} >&2
exit 1
}
# See man 1 tput, man 5 terminfo.
red=$(tput setaf 1) || die
noattr=$(tput sgr0) || die
# If tput is unavailable (very unlikely), use the hardcoded values as follows.
# The ANSI codes always start with the escape character (octal 33).
#esc='\033'
#red="${esc}[031m"
#noattr="${esc}[0m"
# %b causes printf to expand backslash escape sequences. See info bash printf.
printf '%b%s%b\n%s\n' "$red" 'red text' "$noattr" 'normal text'
Note the use of printf. Don't use echo in new software.
I'm developing some shell script. please help me
I want to highlight specific strings with FUNCTION like this.
#!/bin/bash
function echoWithColor(){
local InputString=${1}
tput bold;tput setaf 2 # make words color to bright green
echo ${InputString}
tput sgr0 # make words color to default
}
echoWithColor "Hello everyone!"
this works, but I want to add some blank (space text) at end of line like
echo -n "Input your age : "; read AGE
[root#mycomputer scripts]# ./script.sh
Input your age : 9999
but
#!/bin/bash
function echoWithColor(){
local InputString=${1}
tput bold;tput setaf 2 # make words color to bright green
echo ${InputString}
tput sgr0 # make words color to default
}
echoWithColor "-n Input your age : "; read AGE
[root#mycomputer scripts]# ./script.sh
Input your age :9999
Nope
.
#!/bin/bash
function echoWithColor(){
local InputString=${1}
tput bold;tput setaf 2 # make words color to bright green
echo ${InputString}
tput sgr0 # make words color to default
}
echoWithColor "-n \"Input your age : \""; read AGE
[root#mycomputer scripts]# ./script.sh
"Input your age :"9999
Escaping? Nope.
How can I add blank text at end of line?
This happens because you don't quote your variables:
var=" my value "
echo $var without quotes # writes: my value without quotes
echo "$var with quotes" # writes: my value with quotes
Since you want to pass pass multiple strings to echo (both -n and Input your age), you should rewrite the function to take multiple arguments ("$#") instead of just one ($1), and then make sure to quote when using them:
#!/bin/bash
echoWithColor(){
local InputStrings=( "$#" )
tput bold;tput setaf 2 # make words color to bright green
echo "${InputStrings[#]}"
tput sgr0 # make words color to default
}
echoWithColor -n "Input your age: "
Now echoWithColor works exactly like echo, and preserves all spaces you pass to it.
Just add space ' ' in echo ${InputString} command in function just like below
#!/bin/bash
function echoWithColor(){
local InputString=${1}
tput bold;tput setaf 2 # make words color to bright green
echo ${InputString}' '
tput sgr0 # make words color to default
}
echoWithColor "-n Input your age : "; read AGE
My bash-script looks as following:
echo "Description:"
while [ $finishInput -eq 0 ]; do
read tmp
desc="$desc"$'\n'"$tmp"
if [ -z "$tmp" ]; then
finishInput="1"
fi
done
echo -n "Maintainer:"
read maintainer
It reads to the desc var until a empty line is passed. After that, i want to read in other stuff.
When executing my current script it looks like this:
Description:
Line 1
Line 2
Maintainer:
I would like to overwrite the last empty line with the "Maintainer:".
I searched for a solution but only found suggestions which were like
echo -n "Old line"
echo -e "\r new line"
which stays on the line and overwrites it. This is not possible in my case.
In your example you delete the text at the same line. When you want to return to the previous line use \e[1A, and to clear that line, use \e[K:
echo 'Old line'
echo -e '\e[1A\e[Knew line'
When you want to go N lines up, use \e[<N>A
Found a great guide on escape sequences and wanted to expand on some of the discussions here.
When you write out to a terminal, you move an invisible cursor around, much like you do when you write in any text editor. When using echo, it will automatically end the output with a new line character which moves the cursor to the next line.
$ echo "Hello" && echo " World"
Hello
World
You can use -n to prevent the new line and if you echo again after this, it will append it to the end of that line
$ echo -n "Hello" && echo " World"
Hello World
The cursor remains where it was so, on it's own, we can't use -n to overwrite the previous line, we need to move the cursor to the left. To do that we need to give it an escape sequence, which we let echo know we're going to use with -e and then move the cursor by providing a return carriage \r which puts the cursor at the beginning of the line.
$ echo -n "Hello" && echo -e "\rWorld"
World
That may look like it worked, but see what happens with
$ echo -n "A longer sentance" && echo -e "\rShort sentance"
Short sentancence
See the extra characters? Simply writing over the line only changes the characters where we wrote them.
To fix this, the accepted answer above uses the escape character \e[0K to erase everything after the cursor, after the cursor has moved left. i.e. \r move to beginning \e[0K erase to end.
$ echo -n "A longer sentance" && echo -e "\r\e[0KShort sentance"
Short sentance
Important \e to begin escape sequences works in zsh but not in sh and not necessarily in bash, however \033 works in all of them. If you want your script to work anywhere, you should preference \033
$ echo -n "A longer sentance" && echo -e "\r\033[0KShort sentance"
Short sentance
But escape characters can provide even more utility. For example \033[1A moves the cursor to the previous line so we don't need the -n on the previous echo:
$ echo "A longer sentance" && echo -e "\r\033[1A\033[0KShort sentance"
Short sentance
\r move to the beginning \033[1A move up \033[0K erase to the end
Finally, this is all a bit messy in my book, so you can turn this into a function:
overwrite() { echo -e "\r\033[1A\033[0K$#"; }
Using $# just puts all the parameters of the function into the string
$ echo Longer sentance && overwrite Short sentence
Short sentence
I built a function from Dennis Williamsons Comment:
function clearLastLine() {
tput cuu 1 && tput el
}
Thanks to Dennis Williamson
If you echo without the newline character echo -n "Something", you can use \r with your next echo to move the 'cursor' to the beginning of the line echo -e "\\rOverwrite something".
#!/bin/bash
CHECK_MARK="\033[0;32m\xE2\x9C\x94\033[0m"
echo -e "\n\e[4mDoing Things\e[0m"
echo -n "doing thing 1..."
sleep 1
echo -e "\\r${CHECK_MARK} thing 1 done"
Just be aware that if your new string is shorter that your old string, the tail of your old string will still be visible. Note the done.. in the gif above.
If you want to run a script in a loop and not blow up your scrollback, you can use the following pattern:
while sleep 10s; do
echo -n $(script)
echo -n -e "\e[0K\r"
done
Just replace the script command with your own.
#!/bin/bash
echo "Description:"
while test -z $finishInput; do
read -s tmp
desc="$desc"$'\n'"$tmp"
if [ -z "$tmp" ]; then
finishInput=1
else
echo $tmp
fi
#echo "fi="$finishInput;
done
echo -n "Maintainer:"
read maintainer
This solution avoids the empty line, but input is not echoed before the lines are complete.
Hint: My version of bash did not accept "[ $finishInput -eq 0 ]".
I'm creating a bash script and would like to display a message with a right aligned status (OK, Warning, Error, etc) on the same line.
Without the colors, the alignment is perfect, but adding in the colors makes the right aligned column wrap to the next line, incorrectly.
#!/bin/bash
log_msg() {
RED=$(tput setaf 1)
GREEN=$(tput setaf 2)
NORMAL=$(tput sgr0)
MSG="$1"
let COL=$(tput cols)-${#MSG}
echo -n $MSG
printf "%${COL}s" "$GREEN[OK]$NORMAL"
}
log_msg "Hello World"
exit;
I'm not sure why it'd wrap to the next line -- having nonprinting sequences (the color changes) should make the line shorter, not longer. Widening the line to compensate works for me (and BTW I recommend using printf instead of echo -n for the actual message):
log_msg() {
RED=$(tput setaf 1)
GREEN=$(tput setaf 2)
NORMAL=$(tput sgr0)
MSG="$1"
let COL=$(tput cols)-${#MSG}+${#GREEN}+${#NORMAL}
printf "%s%${COL}s" "$MSG" "$GREEN[OK]$NORMAL"
}
You have to account for the extra space provided by the colors.
log_msg() {
RED=$(tput setaf 1)
GREEN=$(tput setaf 2)
NORMAL=$(tput sgr0)
MSG="$1"
STATUS="[OK]"
STATUSCOLOR="$GREEN${STATUS}$NORMAL"
let COL=$(tput cols)-${#MSG}+${#STATUSCOLOR}-${#STATUS}
echo -n $MSG
printf "%${COL}s\n" "$STATUSCOLOR"
}