developer tip

printf의 패딩 문자

optionbox 2020. 8. 25. 07:55
반응형

printf의 패딩 문자


프로세스가 실행 중인지 여부를 표시하는 bash 쉘 스크립트를 작성 중입니다.

지금까지 나는 이것을 얻었다 :

printf "%-50s %s\n" $PROC_NAME [UP]

코드는 다음과 같은 출력을 제공합니다.

JBoss                                              [DOWN]

GlassFish                                          [UP]

verylongprocessname                                [UP]

더 읽기 쉽게 만들기 위해 두 필드 사이의 간격을 '-'또는 '*'로 채우고 싶습니다. 필드 정렬을 방해하지 않고 어떻게해야합니까?

내가 원하는 출력은 다음과 같습니다.

JBoss -------------------------------------------  [DOWN]

GlassFish ---------------------------------------  [UP]

verylongprocessname -----------------------------  [UP]

Pure Bash, 외부 유틸리티 없음

이 데모는 완전한 양쪽 맞춤을 수행하지만, 오른쪽의 불규칙한 선을 원하면 두 번째 문자열의 길이를 빼는 것을 생략 할 수 있습니다.

pad=$(printf '%0.1s' "-"{1..60})
padlength=40
string2='bbbbbbb'
for string1 in a aa aaaa aaaaaaaa
do
     printf '%s' "$string1"
     printf '%*.*s' 0 $((padlength - ${#string1} - ${#string2} )) "$pad"
     printf '%s\n' "$string2"
     string2=${string2:1}
done

불행히도이 기술에서 패드 문자열의 길이는 필요하다고 생각하는 가장 긴 길이보다 길도록 하드 코딩되어야하지만, 패드 길이는 표시된대로 변수가 될 수 있습니다. 그러나 패드의 길이에 대한 변수를 사용할 수 있도록 첫 번째 줄을 다음 세 줄로 바꿀 수 있습니다.

padlimit=60
pad=$(printf '%*s' "$padlimit")
pad=${pad// /-}

따라서 패드 ( padlimitpadlength)는 터미널 너비 ( $COLUMNS)를 기반으로 하거나 가장 긴 데이터 문자열의 길이에서 계산 될 수 있습니다 .

산출:

a--------------------------------bbbbbbb
aa--------------------------------bbbbbb
aaaa-------------------------------bbbbb
aaaaaaaa----------------------------bbbb

두 번째 문자열의 길이를 빼지 않고 :

a---------------------------------------bbbbbbb
aa--------------------------------------bbbbbb
aaaa------------------------------------bbbbb
aaaaaaaa--------------------------------bbbb

대신 첫 번째 줄은 동일 할 수 있습니다 (와 유사 sprintf).

printf -v pad '%0.1s' "-"{1..60}

또는 유사하게 더 동적 인 기술 :

printf -v pad '%*s' "$padlimit"

원하는 경우 한 줄에 모두 인쇄 할 수 있습니다.

printf '%s%*.*s%s\n' "$string1" 0 $((padlength - ${#string1} - ${#string2} )) "$pad" "$string2"

퓨어 배쉬. 고정 문자열 'line'의 오프셋으로 'PROC_NAME'값의 길이를 사용합니다.

line='----------------------------------------'
PROC_NAME='abc'
printf "%s %s [UP]\n" $PROC_NAME "${line:${#PROC_NAME}}"
PROC_NAME='abcdef'
printf "%s %s [UP]\n" $PROC_NAME "${line:${#PROC_NAME}}"

이것은 준다

abc ------------------------------------- [UP]
abcdef ---------------------------------- [UP]

사소하지만 작동하는 솔루션 :

echo -e "---------------------------- [UP]\r$PROC_NAME "

이것이 가장 간단한 해결책이라고 생각합니다. 순수 쉘 내장, 인라인 수학 없음. 이전 답변에서 차용합니다.

부분 문자열과 $ {# ...} 메타 변수 만 있습니다.

A="[>---------------------<]";

# Strip excess padding from the right
#

B="A very long header"; echo "${A:0:-${#B}} $B"
B="shrt hdr"          ; echo "${A:0:-${#B}} $B"

생산

[>----- A very long header
[>--------------- shrt hdr


# Strip excess padding from the left
#

B="A very long header"; echo "${A:${#B}} $B"
B="shrt hdr"          ; echo "${A:${#B}} $B"

생산

-----<] A very long header
---------------<] shrt hdr

을 사용하여 공백으로 채울 수있는 방법은 없습니다 printf. 다음을 사용할 수 있습니다 sed.

printf "%-50s@%s\n" $PROC_NAME [UP] | sed -e 's/ /-/g' -e 's/@/ /' -e 's/-/ /'

echo -n "$PROC_NAME $(printf '\055%.0s' {1..40})" | head -c 40 ; echo -n " [UP]"

설명:

  • printf '\055%.0s' {1..40} - Create 40 dashes
    (dash is interpreted as option so use escaped ascii code instead)
  • "$PROC_NAME ..." - Concatenate $PROC_NAME and dashes
  • | head -c 40 - Trim string to first 40 chars

This one is even simpler and execs no external commands.

$ PROC_NAME="JBoss"
$ PROC_STATUS="UP"
$ printf "%-.20s [%s]\n" "${PROC_NAME}................................" "$PROC_STATUS"

JBoss............... [UP]

using echo only

The anwser of @Dennis Williamson is working just fine except I was trying to do this using echo. Echo allows to output charcacters with a certain color. Using printf would remove that coloring and print unreadable characters. Here's the echo-only alternative:

string1=abc
string2=123456
echo -en "$string1 "
for ((i=0; i< (25 - ${#string1}); i++)){ echo -n "-"; }
echo -e " $string2"

output:

abc ---------------------- 123456

of course you can use all the variations proposed by @Dennis Williamson whether you want the right part to be left- or right-aligned (replacing 25 - ${#string1} by 25 - ${#string1} - ${#string2} etc...


Here's another one:

$ { echo JBoss DOWN; echo GlassFish UP; } | while read PROC STATUS; do echo -n "$PROC "; printf "%$((48-${#PROC}))s " | tr ' ' -; echo " [$STATUS]"; done
JBoss -------------------------------------------- [DOWN]
GlassFish ---------------------------------------- [UP]

If you are ending the pad characters at some fixed column number, then you can overpad and cut to length:

# Previously defined:
# PROC_NAME
# PROC_STATUS

PAD="--------------------------------------------------"
LINE=$(printf "%s %s" "$PROC_NAME" "$PAD" | cut -c 1-${#PAD})
printf "%s %s\n" "$LINE" "$PROC_STATUS"

Simple Console Span/Fill/Pad/Padding with automatic scaling/resizing Method and Example.

function create-console-spanner() {
    # 1: left-side-text, 2: right-side-text
    local spanner="";
    eval printf -v spanner \'"%0.1s"\' "-"{1..$[$(tput cols)- 2 - ${#1} - ${#2}]}
    printf "%s %s %s" "$1" "$spanner" "$2";
}

Example: create-console-spanner "loading graphics module" "[success]"

Now here is a full-featured-color-character-terminal-suite that does everything in regards to printing a color and style formatted string with a spanner.

# Author: Triston J. Taylor <pc.wiz.tt@gmail.com>
# Date: Friday, October 19th, 2018
# License: OPEN-SOURCE/ANY (NO-PRODUCT-LIABILITY OR WARRANTIES)
# Title: paint.sh
# Description: color character terminal driver/controller/suite

declare -A PAINT=([none]=`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`);

declare -i PAINT_ACTIVE=1;

function paint-replace() {
    local contents=$(cat)
    echo "${contents//$1/$2}"
}

source <(cat <<EOF
function paint-activate() {
    echo "\$@" | $(for k in ${!PAINT[@]}; do echo -n paint-replace \"\&$k\;\" \"\${PAINT[$k]}\" \|; done) cat;
}
EOF
)

source <(cat <<EOF
function paint-deactivate(){
    echo "\$@" | $(for k in ${!PAINT[@]}; do echo -n paint-replace \"\&$k\;\" \"\" \|; done) cat;    
}
EOF
)

function paint-get-spanner() {
    (( $# == 0 )) && set -- - 0;
    declare -i l=$(( `tput cols` - ${2}))
    eval printf \'"%0.1s"\' "${1:0:1}"{1..$l}
}

function paint-span() {
    local left_format=$1 right_format=$3
    local left_length=$(paint-format -l "$left_format") right_length=$(paint-format -l "$right_format")
    paint-format "$left_format";
    paint-get-spanner "$2" $(( left_length + right_length));
    paint-format "$right_format";
}

function paint-format() {
    local VAR="" OPTIONS='';
    local -i MODE=0 PRINT_FILE=0 PRINT_VAR=1 PRINT_SIZE=2;
    while [[ "${1:0:2}" =~ ^-[vl]$ ]]; do
        if [[ "$1" == "-v" ]]; then OPTIONS=" -v $2"; MODE=$PRINT_VAR; shift 2; continue; fi;
        if [[ "$1" == "-l" ]]; then OPTIONS=" -v VAR"; MODE=$PRINT_SIZE; shift 1; continue; fi;
    done;
    OPTIONS+=" --"
    local format="$1"; shift;
    if (( MODE != PRINT_SIZE && PAINT_ACTIVE )); then
        format=$(paint-activate "$format&none;")
    else
        format=$(paint-deactivate "$format")
    fi
    printf $OPTIONS "${format}" "$@";
    (( MODE == PRINT_SIZE )) && printf "%i\n" "${#VAR}" || true;
}

function paint-show-pallette() {
    local -i PAINT_ACTIVE=1
    paint-format "Normal: &red;red &green;green &blue;blue &magenta;magenta &yellow;yellow &cyan;cyan &white;white &black;black\n";
    paint-format "  Bold: &bold;&red;red &green;green &blue;blue &magenta;magenta &yellow;yellow &cyan;cyan &white;white &black;black\n";
}

To print a color, that's simple enough: paint-format "&red;This is %s\n" red And you might want to get bold later on: paint-format "&bold;%s!\n" WOW

The -l option to the paint-format function measures the text so you can do console font metrics operations.

The -v option to the paint-format function works the same as printf but cannot be supplied with -l

Now for the spanning!

paint-span "hello " . " &blue;world" [note: we didn't add newline terminal sequence, but the text fills the terminal, so the next line only appears to be a newline terminal sequence]

and the output of that is:

hello ............................. world


Simple but it does work:

printf "%-50s%s\n" "$PROC_NAME~" "~[$STATUS]" | tr ' ~' '- '

Example of usage:

while read PROC_NAME STATUS; do  
    printf "%-50s%s\n" "$PROC_NAME~" "~[$STATUS]" | tr ' ~' '- '
done << EOT 
JBoss DOWN
GlassFish UP
VeryLongProcessName UP
EOT

Output to stdout:

JBoss -------------------------------------------- [DOWN]
GlassFish ---------------------------------------- [UP]
VeryLongProcessName ------------------------------ [UP]

Bash + seq to allow parameter expansion

Similar to @Dennis Williamson answer, but if seq is available, the length of the pad string need not be hardcoded. The following code allows for passing a variable to the script as a positional parameter:

COLUMNS="${COLUMNS:=80}"
padlength="${1:-$COLUMNS}"
pad=$(printf '\x2D%.0s' $(seq "$padlength") )

string2='bbbbbbb'
for string1 in a aa aaaa aaaaaaaa
do
     printf '%s' "$string1"
     printf '%*.*s' 0 $(("$padlength" - "${#string1}" - "${#string2}" )) "$pad"
     printf '%s\n' "$string2"
     string2=${string2:1}
done

The ASCII code "2D" is used instead of the character "-" to avoid the shell interpreting it as a command flag. Another option is "3D" to use "=".

In absence of any padlength passed as an argument, the code above defaults to the 80 character standard terminal width.

To take advantage of the the bash shell variable COLUMNS (i.e., the width of the current terminal), the environment variable would need to be available to the script. One way is to source all the environment variables by executing the script preceded by . ("dot" command), like this:

. /path/to/script

or (better) explicitly pass the COLUMNS variable when executing, like this:

/path/to/script $COLUMNS

참고URL : https://stackoverflow.com/questions/4409399/padding-characters-in-printf

반응형