printf의 패딩 문자


107

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

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

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

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

JBoss                                              [DOWN]

GlassFish                                          [UP]

verylongprocessname                                [UP]

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

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

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

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

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

답변:


77

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"

1
printf '% *. * s'... 부분에 대해 설명해 주시겠습니까?
Édouard Lopez

3
@EdouardLopez : 인수 목록에서 첫 번째 별표는 0으로 대체됩니다. 두 번째 별표는 두 번째 인수의 계산 결과로 대체됩니다. 예를 들어 "aaaa"및 "bbbbb"문자열의 결과는 '%0.31s'입니다. 문자열 (마지막 인수)은 점 뒤에 지정된 길이로 잘립니다. 0은 공백 채우기가 출력되지 않도록합니다. 따라서 31 개의 하이픈이 출력됩니다.
추후 공지가있을 때까지 일시 중지되었습니다.

1
:이 페이지는 @Dennis 윌리엄슨의 대답은 이해하는 데 도움이 수 wiki.bash-hackers.org/commands/builtin/printf#modifiers을
에두아르 로페즈에게

{1..60} in need 60 as variable; ... like "var = 60"
Reegan Miranda

@ReeganMiranda :이 기술이 작동하는 방식은 필요한 가장 큰 값으로 값을 하드 코딩하고 padlength출력 할 실제 길이를 선택하는 데 사용 하는 것입니다.
추후 공지가있을 때까지 일시 중지되었습니다.

68

퓨어 배쉬. 고정 문자열 '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]

마법은 $ {line : $ {# PROC_NAME}}로, bash 하위 문자열 추출을 사용하여 PROC_NAME의 문자 수에서 시작하도록 설정된 변수 줄의 지점에서만 반환을 시작합니다. tldp.org/LDP/abs/html/string-manipulation.html#SUBSTREXTR01
cwingrav

PROC_NAME이미 이스케이프되지 않는 한 공백이 있는 경우에는 처리되지 않습니다 . 각각 두 개의 토큰이있는 한 줄과 변수에있는 두 개의 공백으로 구분 된 토큰에 대해 [UP]이 표시되고 line텍스트 끝에 입력 문자열의 총 길이를 뺀 한 줄이 표시됩니다. 따라서 복잡한 스크립트에서 수행 할 경우 흥미롭고 잠재적으로 안전하지 않은 버그로 이어질 수 있으므로주의하십시오. 그렇지 않으면 짧고 간단합니다. :)
dodexahedron

19

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

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

4
그러나 터미널에서만. 출력이 파일로 전송되면 엉망이됩니다.
thkala 2010

5
그래서 당신은 사소한 해결책에서 정말로 무엇을 기대합니까?!? 출력 리디렉션과 함께 전체 작업?!? ] : P
Nicola Leoni

14

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

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

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

12

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

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

7
+1 PROC_NAME에 대시가 포함 된 경우 문제가 있습니다. @ :를 추가하면 쉽게 해결할 수 있습니다.printf "%-50s@%s\n" ${PROC_NAME}@ [UP] | sed -e 's/ /-/g' -e 's/-@/ /' -e 's/@-/ /'
thkala

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

설명:

  • printf '\055%.0s' {1..40}-40 개의 대시 생성
    (대시는 옵션으로 해석되므로 대신 이스케이프 된 ASCII 코드 사용)
  • "$PROC_NAME ..." -$ PROC_NAME 및 대시 연결
  • | head -c 40 -문자열을 처음 40 자까지 자르기

이상한, 내가 할 때 printf 'x' {1..40}그것은 단지 하나의 출력 x음을
크리스티안

@Krystian은 형식을 복사하지 않았기 때문입니다 :`printf 'x % .0s'{1..40}`는 40 x초를 인쇄합니다
artm

대시가 옵션으로 해석되는 것을 방지하기 위해 이중 대시를 사용하여 나머지는 옵션이 아닌 인수 printf -- "-%.0s" {1..40}
임을 알릴 수 있습니다

7

이것은 더 간단하고 외부 명령을 실행하지 않습니다.

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

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

5

간단하지만 작동합니다.

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

사용 예 :

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

표준 출력으로 출력 :

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

4

사용 echo

@Dennis Williamson의 anwser는 에코를 사용하여 이것을 시도하는 것을 제외하고는 잘 작동합니다. Echo를 사용하면 특정 색상의 문자를 출력 할 수 있습니다. printf를 사용하면 색상이 제거되고 읽을 수없는 문자가 인쇄됩니다. 다음은 echo유일한 대안입니다.

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

산출:

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

물론 당신은 당신이 오른쪽 부분이 왼쪽 또는 교체 (오른쪽 정렬 될 것인지 @Dennis 윌리엄슨에 의해 제안 된 모든 변화 사용할 수 있습니다 25 - ${#string1}25 - ${#string1} - ${#string2}등 ...


2

다음은 또 하나입니다.

$ { 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]

2

고정 된 열 번호에서 채움 문자를 끝낼 경우 오버 패드 및 cut길이로 끝낼 수 있습니다 .

# 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"

2

간단한 콘솔 스팬 / 채우기 / 패드 / 패딩 자동 크기 조정 / 크기 조정 방법 및 예.

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";
}

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

이제 여기입니다 완벽한 기능을 갖춘 컬러 문자 터미널 - 스위트 스패너와 색상과 스타일 서식 첨부 캐릭터 라인을 인쇄에 관해서 모든 것을 않습니다.

# 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";
}

색상을 인쇄하려면 간단합니다. paint-format "&red;This is %s\n" red 나중에 굵게 표시 할 수 있습니다.paint-format "&bold;%s!\n" WOW

함수에 대한 -l옵션 paint-format은 텍스트를 측정하므로 콘솔 글꼴 메트릭 작업을 수행 할 수 있습니다.

기능에 대한 -v옵션 paint-format은 다음과 동일하게 작동 printf하지만 함께 제공 될 수 없습니다.-l

이제 스패닝을 위해 !

paint-span "hello " . " &blue;world" [참고 : 줄 바꿈 터미널 시퀀스를 추가하지 않았지만 텍스트가 터미널을 채우므로 다음 줄은 줄 바꿈 터미널 시퀀스로만 나타납니다.]

그 결과는 다음과 같습니다.

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


0

매개 변수 확장을 허용하는 Bash + seq

@Dennis Williamson 답변과 비슷하지만 seq가능한 경우 패드 문자열의 길이를 하드 코딩 할 필요가 없습니다. 다음 코드에서는 변수를 위치 매개 변수로 스크립트에 전달할 수 있습니다.

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

ASCII 코드 "2D"는 문자 "-"대신 사용되어 쉘이 명령 플래그로 해석하는 것을 방지합니다. 또 다른 옵션은 "="를 사용하는 "3D"입니다.

인수로 전달 된 padlength가없는 경우 위의 코드는 기본적으로 80 자 표준 터미널 너비로 설정됩니다.

bash 쉘 변수 COLUMNS(즉, 현재 터미널의 너비)를 활용하려면 환경 변수를 스크립트에서 사용할 수 있어야합니다. 한 가지 방법은 .다음과 같이 ( "dot"명령) 앞에 나오는 스크립트를 실행하여 모든 환경 변수를 소싱하는 것입니다 .

. /path/to/script

또는 (더 나은) COLUMNS다음과 같이 실행할 때 변수를 명시 적으로 전달하십시오 .

/path/to/script $COLUMNS
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.