bash script , echo output in box
As your shebang and syntax indicates unportable bash
, I prefer it this way:
function box_out()
{
local s="$*"
tput setaf 3
echo " -${s//?/-}-
| ${s//?/ } |
| $(tput setaf 4)$s$(tput setaf 3) |
| ${s//?/ } |
-${s//?/-}-"
tput sgr 0
}
Of course, you can optimize it if you wish.
Update as requested in comment, to handle multiline text too.
function box_out()
{
local s=("$@") b w
for l in "${s[@]}"; do
((w<${#l})) && { b="$l"; w="${#l}"; }
done
tput setaf 3
echo " -${b//?/-}-
| ${b//?/ } |"
for l in "${s[@]}"; do
printf '| %s%*s%s |\n' "$(tput setaf 4)" "-$w" "$l" "$(tput setaf 3)"
done
echo "| ${b//?/ } |
-${b//?/-}-"
tput sgr 0
}
Call it with multiple parameters, like box_out 'first line' 'more line' 'even more line'
.
So, my solution is not quite the same as yours, but strictly speaking it prints a box around the text, and the implementation is a bit simpler so I thought I'd share.
banner() {
msg="# $* #"
edge=$(echo "$msg" | sed 's/./#/g')
echo "$edge"
echo "$msg"
echo "$edge"
}
And here it is in action:
$ banner "hi"
######
# hi #
######
$ banner "hi there"
############
# hi there #
############
Just plain text, no fancy ansi colors or anything.
Boxes!
$ echo 'Love Unix & Linux' | boxes -d stone -p a2v1
+---------------------+
| |
| Love Unix & Linux |
| |
+---------------------+
Since boxes
requires text to be sent to it as a file, I recommend using the syntax in the above example where the text is piped into boxes
through echo
.
"Why boxes
?"
Using it is easy. Just tell it what border design you want to use and how you want it to appear, and you're done.
Of course if you want to be creative, you can make your own designs. It's really easy and fun to do. The only drawback with using boxes
is that I haven't figured out how to center a box generated with boxes
to align center with the screen, though there is a bash hack for that
"What about color?"
The original question had demonstrated the use of color codes. So it only seems right to show how boxes
would handle this.
While tput
is popular, I consider myself an old school Bash person and still like using the escape commands. I'm sure there are a few folks here at StackExchange that would argue against it, but to each their own. Regardless, I am willing to set aside my personal preference of doing this to include another example doing it the tput
way.
Naturally, I think the first step is to set the color of the inside text. So let's do that first.
printf "$(tput setaf 4)Love Unix & Linux$(tput sgr 0)" | boxes -d stone -p a2v1
+---------------------------------+
| |
| Love Unix & Linux |
| |
+---------------------------------+
If this were in the terminal, Love Unix & Linux
would be blue...however as you can see, boxes
did not handle this well. So what wen't wrong?
printf "$(tput setaf 4)Love Unix & Linux$(tput sgr 0)" | boxes -d stone -p a2v1 | cat -A
+---------------------------------+$
| |$
| ^[[34mLove Unix & Linux^[(B^[[0m |$
| |$
+---------------------------------+$
A closer inspection by showing the hidden characters with cat -A
shows boxes
assumed the length of the box to include the length of text AND the escape characters.
It should be noted though, that if you use a program like lolcat
outside of boxes
, the output looks like this
$ echo 'Love Unix & Linux' | boxes -d stone -p a2v1 | lolcat -f
+---------------------+
| |
| Love Unix & Linux |
| |
+---------------------+
but with the box and text in rainbow colors.
It's a good thing I hadn't made my own border designs that included color codes in them as of yet, as I would imagine the border designs would be fallable to the same problems.
Centering Text, ASCII Art, and BOXES!!!
The other flaw Figured it out.boxes
has is that if you know how to use Bash to center text to the with of the terminal, boxes
will still align the box to the left of the screen.
When you want to center text that is not in a box, you can simply use
center(){
COLS=$(tput cols) # use the current width of the terminal.
printf "%*s\n" "$(((${#1}+${COLS})/2))" "$1"
}
And use that to center a single line of text.
center 'Love Unix & Linux'
For ascii-art where multiple lines are used and must be fixed in place there is this option.
# Rather than removing text, even things out by padding lines with spaces
draw_banner(){
local banner="$1"
# Banner Width
BW=$(cat $banner | awk '{print length}' | sort -nr | head -1)
while IFS= read -r line; do
line=$(echo "${line}" | sed -n -e 's/^ / /g;p')
line=$(printf "%-${BW}s" "${line}")
center "${line}" # our center function from earlier.
done < "$banner"
}
draw_banner "path/to/your_ascii_art_logo.txt"
But if you like to use things like figlet
, you don't need to use those functions as the -c
option provides an option to center.
$figfontdir="path/to/figlet/fonts"
$figfont="Alligator"
text_banner(){
COLS=$(tput cols) # use the width of the terminal!
figlet -d "$figfontdir" -f "$figfont" -k -w $COLS -c "$1"
}
text_banner 'Love Unix & Linux'
For boxes
, we do something similar to draw_banner()
but we need to pipe the data!
# Center a box created with `boxes
# It's like draw_banner, but `<<<` reads a string of data rather than a file.
$boxfile="/path/to/your_box_designs.box" # or ".txt". I like ".box".
$boxdesgin="stone"
center_box(){
local data="$(</dev/stdin)" # Read from standard input
# Banner Width
BW=$(cat <<< ${data} | awk '{print length}' | sort -nr | head -1)
while IFS= read -r line; do
line=$(echo "${line}" | sed -n -e 's/^ / /g;p')
line=$(printf "%-${BW}s" "${line}")
center "${line}" # our center command from earlier.
done <<< "${data}"
}
(
# A bunch of stuff to center!
) | boxes -f $boxfile -d $boxdesign -a hcvcjc | center_box
Outstanding issues
Fixing both of these issues the UTF/ANSI character issue would not only make boxes
a better solution to encapsulating text in an ASCII box, but allow for a creative alternative that could be called upon instead of hand-coding boxes.