Search code examples
bashconsolestdout

bash console output print layout


I want to make my writing to the console output in a nice tidy human readable. here is how it looks now:

====================== Sat Apr 16 12:57:17 EDT 2022 ======================
==========================================================================
======================  Leopard - Download from S3  ======================
==========================================================================
==========================================================================
======================  Leopard - Decompressing  ======================
==========================================================================
total 1349872
drwxr-xr-x  2 root root     12288 Apr 16 12:57 .
drwxrwxrwt. 4 root root       102 Apr 16 12:57 ..
-rw-r--r--  1 root root 185070885 Apr 16 12:03 asdasdasd.sql.gz
-rw-r--r--  1 root root  40344632 Apr 16 12:03 asdasdas.sql.gz
-rw-r--r--  1 root root     26631 Apr 16 12:03 asdad.sql.gz
-rw-r--r--  1 root root      1679 Apr 16 12:03 asdasd.sql.gz
-rw-r--r--  1 root root      1237 Apr 16 12:03 asd.sql.gz
-rw-r--r--  1 root root   5241900 Apr 16 12:03 asdasd.sql.gz
-rw-r--r--  1 root root      1144 Apr 16 12:03 asdasasd.sql.gz
-rw-r--r--  1 root root    489312 Apr 16 12:03 asdasd.sql.gz
-rw-r--r--  1 root root      1138 Apr 16 12:03 asdasdasd.sql.gz
==========================================================================
======================  NewYorkCity - Download from S3  ======================
==========================================================================
==========================================================================
======================  NewYorkCity - Unloading SSL Example  ======================
==========================================================================
total 1349872
drwxr-xr-x  2 root root     12288 Apr 16 12:57 .
drwxrwxrwt. 4 root root       102 Apr 16 12:57 ..
-rw-r--r--  1 root root 185070885 Apr 16 12:03 asdasdasd.sql.gz
-rw-r--r--  1 root root  40344632 Apr 16 12:03 asdasdas.sql.gz
-rw-r--r--  1 root root     26631 Apr 16 12:03 asdad.sql.gz
-rw-r--r--  1 root root      1679 Apr 16 12:03 asdasd.sql.gz
-rw-r--r--  1 root root      1237 Apr 16 12:03 asd.sql.gz
-rw-r--r--  1 root root   5241900 Apr 16 12:03 asdasd.sql.gz
-rw-r--r--  1 root root      1144 Apr 16 12:03 asdasasd.sql.gz
-rw-r--r--  1 root root    489312 Apr 16 12:03 asdasd.sql.gz
-rw-r--r--  1 root root      1138 Apr 16 12:03 asdasdasd.sql.gz

I want that all the === line will be in the same length, and the text always in the center with 1 space on each side

Will appreciate assistance here :)

UPDATE / EDIT: The original script is something like that:

eecho () { echo ==========================================================================; }

echo_stage () {
  START=1
  END=11
  for (( c=$START; c<=$END; c++ ))
  do
    printf == '-%.0s'
  done
  echo -n " " $1 " "
  for (( c=$START; c<=$END; c++ ))
  do
    printf == '-%.0s'
  done
  echo
}

stage() {
  eecho
  echo_stage "$1" "$2"
  eecho
}

print_date () { echo "======================" $(date) "======================"; }


reload_db() {
  print_date
  rm -rf /var/tmp/db
  mkdir -p /var/tmp/db
  stage "DB - Download from S3"
  aws s3 sync s3://db-backup/latest/ /var/tmp/db --profile=papilon --quiet
  stage "DB - Decompressing"
  pigz -d /var/tmp/db/*
  stage "DB - Restoring Data"
  cd /var/tmp/db
  stage "DB - Restoring Tables"
  for i in `ls -1 *.sql | grep -v "_view.sql"`;do echo $i;mysql db < $i;done
  stage "DB - Restoring Views"
  for i in `ls -1 *.sql | grep  "_view.sql"`;do echo $i;mysql db < $i;done
  stage "DB - Clean up"
  rm -rf /var/tmp/db
  print_date
}


reload_db

Solution

  • Because you have now posted your script, I will add a new answer for pure bash.

    title() {
        local text pad
    
        (( ${#1} > 70 )) && { echo "$1"; return; }
        text=${1:+ }$1${1:+ }
    
        pad=$( eval "printf %.1s ={1..$(( ( 74 - ${#text} ) / 2 ))}" )
    
        echo "$pad$text$pad$( (( ${#text} % 2 )) && printf = )"
    }
    

    For title 'foo bar', this function prints foo bar (or any string up to 70 characters) with a space either side, centered and padded to 74 columns with =. If the string is longer than 70 characters it's too long to pad, so it's printed as is.

    ================================ foo bar =================================
    

    With no argument, or an empty argument, it prints a solid line of 74 =:

    ==========================================================================
    
    • You can swap = for any single ASCII character.
    • You can swap 74 for any even number. Also for an odd number, if you change && printf to || printf in the last line. (also change the 70 to N - 4)
    • You can call title once for the date, or three times for the larger three line banners (see banner below).

    Explanation:

    • ${#text} is bash for "length of $text".

    • ${1:+ } expands to a space, unless $1 is empty or unset. This allows us to add spaces to either end of the string, or exclude them for an empty string.

    • ={1..10} expands to =1 =2 =3 ... =10, and printf %.1s prints the first character of each string. Combining these allows us to repeat a string (=) N times.

    • But we can't use a variable (or arithmetic) in {1..10} normally. So we need eval.

    • You probably heard eval is bad, and a security risk. That's often true, but here we are not passing any unknown data to eval (such as user input), and it's safe from code injection. (${#text} always expands to a single number)

    • So we make two bars, of length (74 - text-length) / 2, adding another = to the second bar if the text length is an odd number.

    I also made a few changes to your script which you might consider. Apart from the date and titles, these have nothing to do with the padding. The title and banner functions will work on your old script.

    title() {
        local text pad
    
        (( ${#1} > 70 )) && { echo "$1"; return; }
        text="${1:+ }$1${1:+ }"
    
        pad=$( eval "printf %.1s ={1..$(( ( 74 - ${#text} ) / 2 ))}" )
    
        echo "$pad$text$pad$( (( ${#text} % 2 )) && printf = )"
    }
    
    banner() {
        title
        title "$1"
        title
    }
    
    reload_db() {
    
        local i
    
        title "$(date)"
    
        rm -rf /var/tmp/db || exit 1
        mkdir -p /var/tmp/db || exit 1
    
        banner 'DB - Download from S3'
        aws s3 sync s3://db-backup/latest/ /var/tmp/db --profile=papilon --quiet
    
        banner 'DB - Decompressing'
        pigz -d /var/tmp/db/*
    
        banner 'DB - Restoring Data'
        cd /var/tmp/db || exit 1
    
        banner 'DB - Restoring Tables'
        GLOBIGNORE='*_view.sql'
        for i in *.sql; do
            echo "$i"
            mysql db < "$i"
        done
       GLOBIGNORE=
    
        banner 'DB - Restoring Views'
        for i in *_view.sql; do
            echo "$i"
            mysql db < "$i"
        done
    
        banner 'DB - Clean up'
        rm -rf /var/tmp/db
    
        title "$(date)"
    }
    

    At the very least, you should exit early if cd, mkdir, or the first rm fail. Also, looping over an unquoted ls command sub is a bad idea. Instead you can use glob expansion (or find).

    You could also concatenate the SQL scripts, but this only works if all the commands end with a semicolon (see Run multiple sql files in mysql batch):

        big_title 'DB - Restoring Tables'
        GLOBIGNORE='*_view.sql'
        printf '%s\n' *.sql
        cat *.sql | mysql db
        GLOBIGNORE=
    
        big_title 'DB - Restoring Views'
        printf '%s\n' *_view.sql
        cat *_view.sql | mysql db