Keep text in place regardless output bash printf

ぐ巨炮叔叔 提交于 2021-01-27 19:34:01

问题


This happens when numbers gets bigger...Just started with this mess. Text being pushed around by bigger outputs...how do I contain this mess?

       Accounts......: 5      Mail..........: 7
       Banned........: 0      Pets..........: 1
       Online........: 0      Tickets.......: 0
       Guilds........: 1      Corpses.......: 0      PvP.......: 0
       Members.......: 1      Characters....: 10      Gifts.....: 4   <-----HOWTO Reserve/Preserve spaces ?? ??

Should look like this:

       Accounts......: 5      Mail..........: 7
       Banned........: 0      Pets..........: 1
       Online........: 0      Tickets.......: 0
       Guilds........: 1      Corpses.......: 0      PvP.......: 0
       Members.......: 1      Characters....: 10     Gifts.....: 4

Now this mess looks like this:


ch_count=$(mysql --defaults-extra-file="$sql_mycnf" -N --execute="SELECT count(*) FROM $db_characters.characters;"); &> /dev/null


cm_char="\033[0mCharacters\e[0;32m....:\033[0m $ch_count\e[31m"

line="      "

           $cm_acco$line$cm_mail
           $cm_bann$line$cm_pets
           $cm_onli$line$cm_tick
           $cm_guil$line$cm_corp$line$cm_pvps
           $cm_memb$line$cm_char$line$cm_gifts

On another server there is same outputs but because they are smaller it looks fine:

           Accounts......: 4      Mail..........: 0
           Banned........: 0      Pets..........: 0
           Online........: 0      Tickets.......: 0
           Guilds........: 0      Corpses.......: 0      PvP.......: 0
           Members.......: 0      Characters....: 2      Gifts.....: 0

Edit this line to make it work? Is this correct place to begin?

cm_char="\033[0mCharacters\e[0;32m....:\033[0m $ch_count\e[31m"

Kill me.


回答1:


Make 2 functions that will format the fields and use them:

dot_field() {
   # todo Change implementation when field can be 2 words with a space in between
   printf "%-14.14s:" "$1" | tr ' ' '.'
}

space_number() {
   printf "%-7.7s" "$1"
}

printline() {
   # Todo: add logic when only 4 parameters are given
   echo "       $(dot_field $1) $(space_number $2)$(dot_field $3) $(space_number $4)$(dot_field $5) $(space_number $6)"
}

printline "Guilds" 1 "Corpses" 0 "PvP" 0
printline "Members" 1 "Characters" 10 "Gifts" 4
printline "LongFieldName" 1 "High" 999999 "X" 2

EDIT: Adding colors.

You don't want to have your code full of escape codes for the colors. It depends on the full context how you would like to structure your color codes, I give an example for the limited context of the question. It should give you an idea how you can make something like this for yourself.

init_colors() {
    reset=$(tput sgr0)
    bold=$(tput bold)
    black=$(tput setaf 0)
    red=$(tput setaf 1)
    green=$(tput setaf 2)
    yellow=$(tput setaf 3)
    blue=$(tput setaf 4)
    magenta=$(tput setaf 5)
    cyan=$(tput setaf 6)
    white=$(tput setaf 7)
    user_color=$bold
}

# colorstring reads from stdin and uses parameter 1 as an escape sequence
# with more parameters the first is used as a color, the other as the string to be modified
# It will set colors until the last space sequence
colorstring() {
   case $# in
   0) # invalid
      echo "colorstring called without parameters"
   ;;
   1)
      sed -r "s/^.*[^ ]/$1&${reset}/"
      ;;
   *)
      color="$1"
      shift
      sed -r "s/^.*[^ ]/${color}&${reset}/" <<< "$@"
   ;;
   esac

}

dot_field() {
   # todo Change implementation when field can be 2 words with a space in between
   printf "%-14.14s" "$1" | colorstring ${cyan} | tr ' ' '.'
   # The : must be printed in a second statement when you don't want cyan dots.
   printf ':'
}

space_number() {
   printf "%-7.7s" "$1" | colorstring ${red}
}

printline() {
   echo "       $(dot_field $1) $(space_number $2)$(dot_field $3) $(space_number $4)$(dot_field $5) $(space_number $6)"
}

# init the color variables
init_colors
# Next echo not needed, just testing the new colorstring function
echo "$(colorstring ${blue} blue string) $(colorstring ${red} red car) $(colorstring ${white} white snow) $(colorstring ${yellow} yellow marker) $(colorstring ${cyan} cyan) "
printline "Guilds" 1 "Corpses" 0 "PvP" 0
printline "Members" 1 "Characters" 10 "Gifts" 4
printline "LongFieldName" 1 "High" 999999 "X" 2



回答2:


As you may alredy know there are some spesial system sequinces that controll output to terminal. This for example will turn text red '\e[31m' and this will print text in certain column\line '\e[${LINE};${COLUMN}H'. So we will use that. First i will create this 'data' array with "name value" pairs to simulate your case.

data=(
    "Accounts   5"
    "Banned     10"
    "Online     40"
    "Guilds     4"
    "Members    1"
    "Mail       71"
    "Pets       43"
    "Tickets    0"
    "Corpses    101"
    "Characters 10"
    "PvP        0"
    "Gifts      4"
)

I'm using this table when working with text output so lets used it to

#--------------------------------------------------------------------+
#Color picker, usage: printf ${BLD}${CUR}${RED}${BBLU}"Hello!)"${DEF}|
#-------------------------+--------------------------------+---------+
#       Text color        |       Background color         |         |
#-----------+-------------+--------------+-----------------+         |
# Base color|Lighter shade|  Base color  | Lighter shade   |         |
#-----------+-------------+--------------+-----------------+         |
BLK='\e[30m'; blk='\e[90m'; BBLK='\e[40m'; bblk='\e[100m' #| Black   |
RED='\e[31m'; red='\e[91m'; BRED='\e[41m'; bred='\e[101m' #| Red     |
GRN='\e[32m'; grn='\e[92m'; BGRN='\e[42m'; bgrn='\e[102m' #| Green   |
YLW='\e[33m'; ylw='\e[93m'; BYLW='\e[43m'; bylw='\e[103m' #| Yellow  |
BLU='\e[34m'; blu='\e[94m'; BBLU='\e[44m'; bblu='\e[104m' #| Blue    |
MGN='\e[35m'; mgn='\e[95m'; BMGN='\e[45m'; bmgn='\e[105m' #| Magenta |
CYN='\e[36m'; cyn='\e[96m'; BCYN='\e[46m'; bcyn='\e[106m' #| Cyan    |
WHT='\e[37m'; wht='\e[97m'; BWHT='\e[47m'; bwht='\e[107m' #| White   |
#----------------------------------------------------------+---------+
# Effects                                                            |
#--------------------------------------------------------------------+
DEF='\e[0m'   #Default color and effects                             |
BLD='\e[1m'   #Bold\brighter                                         |
DIM='\e[2m'   #Dim\darker                                            |
CUR='\e[3m'   #Italic font                                           |
UND='\e[4m'   #Underline                                             |
INV='\e[7m'   #Inverted                                              |
COF='\e[?25l' #Cursor Off                                            |
CON='\e[?25h' #Cursor On                                             |
#--------------------------------------------------------------------+
# Text positioning, usage: XY 10 10 "Hello World!"                   |
XY   () { printf "\e[${2};${1}H${3}"; } #                            |
#--------------------------------------------------------------------+
# Print line, usage: line - 10 | line -= 20 | line "Hello World!" 20 |
line () { printf -v LINE "%$2s"; printf -- "${LINE// /$1}"; } #      |
# Create sequence like {0..X}                                        |
cnt () { printf -v _N %$1s; _N=(${_N// / 1}); printf "${!_N[*]}"; } #|
#--------------------------------------------------------------------+

There are all basic colors set as vars to easily insert in text and some usefull functions like XY i'll use it to print text in serrtain position.

Lets set some vars

space_betwen=7  # space betwen columns
X=$space_betwen # starting X(column) position
Y=10            # starting Y(line) position

dot_string='...............: ' # dot string to simulate your output
dot_length=${#dot_string}      # this will calculate the length of the dot string

Ok we are ready to go but first lets clear all text from terminal screen

clear

And now we can iterate through data and print text in 3 columns by 4 lines

for item in "${data[@]}"; {
    ((counter++)) # lets count items to know when start next column
    read name value <<< $item # get naame and value from current item
    XY $X $Y "$dot_string$RED$value$DEF" # print dot string and red value
    XY $X $Y "$YLW$name$DEF" # name will be printed ower dots in yelow color
    ((Y++)) # go to next line by increasing Y value
    # chek if we print 4 lines than set Y to start poosition and inc X to space_betwen+dot_length
    ((counter%4)) || { Y=10; ((X+=space_betwen+dot_length)); }
}

The final script will be like this

    #!/bin/bash

    data=(
        "Accounts   5"
        "Banned     10"
        "Online     40"
        "Guilds     4"
        "Members    1"
        "Mail       71"
        "Pets       43"
        "Tickets    0"
        "Corpses    101"
        "Characters 10"
        "PvP        0"
        "Gifts      4"
    )

    . ~/SCR/color   # include color table
    space_betwen=7  # space betwen columns
    X=$space_betwen # starting X(column) position
    Y=10            # starting Y(line) position

    dot_string='...............: ' # dot string to simulate your output
    dot_length=${#dot_string}      # this will calculate the length of the dot string

clear

for item in "${data[@]}"; {
    ((counter++)) # lets count items to know when start next column
    read name value <<< $item # get naame and value from current item
    XY $X $Y "$dot_string$RED$value$DEF" # print dot string and red value
    XY $X $Y "$YLW$name$DEF" # name will be printed ower dots in yelow color
    ((Y++)) # go to next line by increasing Y value
    # chek if we print 4 lines than set Y to start poosition and inc X to space_betwen+dot_length
    ((counter%4)) || { Y=10; ((X+=space_betwen+dot_length)); }
}
XY 1 20 "$DEF" # one more time to move cursor down in the end

And the output will be like this



来源:https://stackoverflow.com/questions/59862020/keep-text-in-place-regardless-output-bash-printf

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!