Search code examples
bashfunctiontextmessagebox

There's a way to show boxed text messages using pure Bash script?


I wrote a function working quite fine using ANSI escape sequences for terminals like VT100 (normally managed by Linux/Unix terminal emulators). I reported below the Bash code.

#!/bin/bash

# Returns the line number if <= 255; otherwais 255.
function getCurPos() {
    local CURPOS
    local -i lineNumb
    echo -e "\E[6n"; read -sdR CURPOS; CURPOS=${CURPOS#*[}
    CURPOS=(${CURPOS//;/ })
    #echo ${CURPOS[*]}
    lineNumb=${CURPOS[0]}
    if [ $lineNumb -gt 255 ]; then
        return 255
    fi
    return $lineNumb
}

# $1: text with "echo -e" interpreted syntax ('\n' means "return")
function messagetBox() {
    local oldIFS=IFS
    unset IFS
    if [ -z $1 ]; then exit 0; fi
    local text=$1
    local -i maxLineLenght=0
    local -i lineLenght=0
    local -i lineNumb=0
    local -i count
    local -i line
    #echo -e "\033[1;31m"
    echo
    while read i; do
        echo -e "| $i ";
        lineLenght=${#i}
        lineLenght+=4
        if [ $lineLenght -gt $maxLineLenght ]; then
            maxLineLenght=$lineLenght
        fi
        lineNumb+=1
    done < <(echo -e $text)
    count=$maxLineLenght
    while [ $count -gt 0 ]; do
        echo -en "-"
        count+=-1
    done
    #echo -en "\033[u"
    echo -en "\033[$((lineNumb+2))A\n"
    count=$maxLineLenght
    while [ $count -gt 0 ]; do
        echo -en "-"
        count+=-1
    done
    echo -en "\033[1B"
    count=$lineNumb
    while [ $count -gt 0 ]; do
        getCurPos
        line=$?
        echo -e "\033[${line};${maxLineLenght}H|"
        count+=-1
    done
    echo -en "\033[${lineNumb}B"
    echo -e "\033[0m"
    IFS=$localIFS
}
~$ source ./sample.sh
~$ messagetBox "Hello\nMy name is Lorenzo, I was born in Italy."
--------------------------------------------
| Hello                                    |
| My name is Lorenzo, I was born in Italy. |
--------------------------------------------
~$

The problem is that for text lines longer than the screen column's number, new lines before the first side line unshape the box:

~$ messagetBox "Hello. My name is Lorenzo, I was born in Italy. Hello. My name is Lorenzo, I was born in Italy."

| Hello. My name is Lorenzo, I was born in Italy. Hello. My name is Loren------------------------------------------------------------
-----------------------------------------------------------------
-----------------------------------------------

There's a way to avoid it?


Solution

  • Here's a better way to draw the box:

    $ cat ./messagetBox
    #!/usr/bin/env bash
    
    # `printf` will expand `\n` to newline and `\t` to tab
    # `pr` will convert any tabs to the appropriate number
    #  of blanks to match the alignment seen in the input.
    printf '%b\n' "$*" |
    pr -e -t |
    awk '
        {
            width = length($0)
            maxWidth = ( width > maxWidth ? width : maxWidth )
            lines[NR] = $0
        }
        END {
            lineBeg = "| "
            lineEnd = " |"
    
            dashes = sprintf("%*s", maxWidth + length(lineBeg) + length(lineEnd), "")
            gsub(/ /,"-",dashes)
    
            print dashes
            for ( lineNr=1; lineNr<=NR; lineNr++ ) {
                line = sprintf("%-*s", maxWidth, lines[lineNr])
                print lineBeg line lineEnd
            }
            print dashes
        }
    '
    

    $ ./messagetBox "Hello\nMy name is Lorenzo, I was born in Italy."
    --------------------------------------------
    | Hello                                    |
    | My name is Lorenzo, I was born in Italy. |
    --------------------------------------------
    

    It isn't clear from your question what you're using all those escape sequences for but just add them to that output as you like if you need them. In particular I kept the populating of line separate from printing so you could add escape sequences around that to change color or whatever inside the box if that's what you're trying to do.