Заполнение символов в printf

Я пишу скрипт оболочки bash для отображения, запущен процесс или нет.

Пока я получил это:

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

Код дает мне такой вывод:

JBoss                                              [DOWN]

GlassFish                                          [UP]

verylongprocessname                                [UP]

Я хочу заполнить пробел между двумя полями с помощью "-" или "*", чтобы сделать его более читаемым. Как мне сделать это, не нарушая выравнивание полей?

Я хочу получить вывод:

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

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

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

13 ответов

Чистый Баш, без внешних утилит

Эта демонстрация полностью оправдывает себя, но вы можете просто опустить вычитание длины второй строки, если вам нужны неровные линии.

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// /-}

Итак, колодка (padlimit а также padlength) может быть основан на ширине терминала ($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"

Чистый Баш. Используйте длину значения 'PROC_NAME' в качестве смещения для фиксированной строки 'line':

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 "%-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]

Там нет никакого способа дополнить что-нибудь, кроме пробелов, используя 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} - создать 40 штрихов
    (тире интерпретируется как опция, поэтому используйте вместо нее экранированный код ascii)
  • "$PROC_NAME ..." - объединить $PROC_NAME и тире
  • | head -c 40 - Обрезать строку до первых 40 символов

Это еще проще и не требует внешних команд.

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

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

с помощью echo только

Ответчик @Dennis Williamson работает просто отлично, за исключением того, что я пытался сделать это с помощью 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 Williamson, независимо от того, хотите ли вы, чтобы правая часть была выровнена по левому или правому краю (заменяя 25 - ${#string1} от 25 - ${#string1} - ${#string2} так далее...

Вот еще один:

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

Если вы заканчиваете символы пэда на некотором фиксированном номере столбца, тогда вы можете 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"

Простая консоль Span/Fill/Pad/Padding с автоматическим масштабированием / изменением размера Метод и пример.

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

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" для использования "=".

Если длина аргумента не указана в качестве аргумента, в приведенном выше коде по умолчанию используется стандартная ширина терминала в 80 символов.

Чтобы воспользоваться преимуществами переменной оболочки bash COLUMNS (т.е. ширина текущего терминала), переменная окружения должна быть доступна для скрипта. Одним из способов является получение всех переменных среды путем выполнения сценария, которому предшествует . (команда "точка"), вот так:

. /path/to/script

или (лучше) явно передать COLUMNS переменная при выполнении, вот так:

/path/to/script $COLUMNS
Другие вопросы по тегам