Printf'de karakterleri doldurma


107

Bir işlemin çalışıp çalışmadığını görüntülemek için bir bash kabuğu komut dosyası yazıyorum.

Şimdiye kadar bunu anladım:

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

Kod bana şu çıktıyı veriyor:

JBoss                                              [DOWN]

GlassFish                                          [UP]

verylongprocessname                                [UP]

Daha okunaklı hale getirmek için iki alan arasındaki boşluğu bir '-' veya '*' ile doldurmak istiyorum. Alanların hizalamasını bozmadan bunu nasıl yapabilirim?

İstediğim çıktı:

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

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

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

Yanıtlar:


77

Pure Bash, harici yardımcı program yok

Bu gösteri tam gerekçelendirme yapar, ancak düzensiz doğru satırlar istiyorsanız ikinci dizenin uzunluğunu çıkarmayı atlayabilirsiniz.

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

Ne yazık ki, bu teknikte, ped dizisinin uzunluğu, ihtiyacınız olacağını düşündüğünüz en uzun olandan daha uzun olacak şekilde kodlanmalıdır, ancak dolgu uzunluğu gösterildiği gibi bir değişken olabilir. Bununla birlikte, pedin uzunluğu için bir değişken kullanabilmek için ilk satırı bu üç satırla değiştirebilirsiniz:

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

Dolayısıyla, ped ( padlimitve padlength) terminal genişliğine ( $COLUMNS) dayalı olabilir veya en uzun veri dizisinin uzunluğundan hesaplanabilir.

Çıktı:

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

İkinci dizenin uzunluğunu çıkarmadan:

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

İlk satır bunun yerine eşdeğer olabilir (şuna benzer sprintf):

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

veya benzer şekilde daha dinamik teknik için:

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

İsterseniz yazdırmayı tek satırda yapabilirsiniz:

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

1
Printf '% *. * S' ... kısmını biraz açıklayabilir misiniz?
Édouard Lopez

3
@EdouardLopez: İlk yıldız işareti, bağımsız değişken listesindeki sıfır ile değiştirilir. İkinci bağımsız değişkendeki hesaplamanın sonucu ikinci yıldız işareti ile değiştirilir. Örneğin "aaaa" ve "bbbbb" dizeleri için sonuç şu şekildedir '%0.31s'. Dize (son bağımsız değişken), noktadan sonra belirtilen uzunlukta kesilir. Sıfır, herhangi bir boşluk dolgusunun çıktı alınmasını engeller. Yani 31 tire çıktı.
sonraki duyuruya kadar duraklatıldı.

1
Bu sayfa, @Dennis Williamson'ın yanıtını anlamanıza yardımcı olabilir: wiki.bash-hackers.org/commands/builtin/printf#modifiers
Édouard Lopez

{1..60} değişken olarak 60'a ihtiyaç var; ... "var = 60" gibi
Reegan Miranda

@ReeganMiranda: Bu tekniğin çalışma şekli, değeri ihtiyacınız olan en büyük değere kodlamanız ve çıktı alınacak padlengthgerçek uzunluğu seçmek için kullanmanızdır .
sonraki duyuruya kadar duraklatıldı.

68

Pure Bash. Sabit dize "line" için ofset olarak "PROC_NAME" değerinin uzunluğunu kullanın:

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

Bu verir

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

Sihir, PROC_NAME içindeki karakter sayısıyla başlayacak şekilde ayarlanan değişken satırının yalnızca bir noktasından geri dönüşü başlatmak için bash alt dize çıkarma işlemini kullanan $ {line: $ {# PROC_NAME}}. tldp.org/LDP/abs/html/string-manipulation.html#SUBSTREXTR01
cwingrav

Bunun PROC_NAME, zaten öncelenmemiş boşlukların olduğu durumu işlemez . Değişkeninizdeki her iki boşlukla ayrılmış simge için her biri iki simge içeren bir satır ve ardından [UP] ve ardından linemetniniz eksi giriş dizenizin toplam uzunluğuyla birlikte tek bir satır elde edersiniz. Bu yüzden dikkatli olun, çünkü bu karmaşık bir komut dosyasıyla yapılırsa ilginç ve potansiyel olarak güvensiz hatalara yol açabilir. Aksi takdirde kısa ve basit. :)
dodexahedron

19

Önemsiz (ama işe yarayan) çözüm:

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

4
Ama sadece bir terminalde. Çıktı bir dosyaya gönderilirse karışıklık olur.
thkala

5
Peki önemsiz bir çözümden gerçekten ne bekliyorsunuz?!? çıktı yönlendirme ile de tam çalışma?!? ]: P
Nicola Leoni

14

Bunun en basit çözüm olduğunu düşünüyorum. Saf kabuk yerleşikleri, satır içi matematik yok. Önceki cevaplardan ödünç alıyor.

Sadece alt dizeler ve $ {# ...} meta değişkeni.

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"

Üretir

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

Üretir

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

12

Boşluk kullanmaktan başka bir şey doldurmanın bir yolu yok printf. Şunları kullanabilirsiniz sed:

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

7
+1 PROC_NAME bir tire içeriyorsa bir sorun var - ek bir @: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]"

Açıklama:

  • printf '\055%.0s' {1..40}- 40 çizgi oluşturun
    (kısa çizgi, seçenek olarak yorumlanır, bu nedenle bunun yerine çıkış karakterli ascii kodunu kullanın)
  • "$PROC_NAME ..." - $ PROC_NAME ve kısa çizgileri birleştirin
  • | head -c 40 - Dizeyi ilk 40 karaktere kırp

Garip, bunu yaptığımda printf 'x' {1..40}yalnızca tek bir xhmmm yazdırıyor
Krystian

@Krystian olduğunu, biçimi kopyalamamışsanız en çünkü: `printf 'x% .0s' {1..40}` baskılar 40 xs
bölümü içindeki

Tire'nin seçenek olarak yorumlanmasını önlemek için çift tire, geri kalanların seçenek olmayan argümanlar olduğunu belirtmek için kullanılabilirprintf -- "-%.0s" {1..40}
artm

7

Bu daha da basittir ve harici komutlar yürütmez.

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

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

5

Basit ama işe yarıyor:

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

Kullanım örneği:

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

Standart çıktıya çıktı:

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

4

echosadece kullanarak

@Dennis Williamson'ın yanıtlayıcısı, bunu eko kullanarak yapmaya çalışmam dışında gayet iyi çalışıyor. Echo, belirli bir renkteki karakterlerin çıktısını almanıza izin verir. Printf kullanmak bu renklendirmeyi kaldırır ve okunamayan karakterleri yazdırır. İşte tek echoalternatif:

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

çıktı:

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

tabii ki doğru parçası sol veya değiştirilmesi (sağa hizalanması isteyip @Dennis Williamson tarafından teklif edilen tüm varyasyonları kullanabilirsiniz 25 - ${#string1}tarafından 25 - ${#string1} - ${#string2}vb ...


2

İşte burada bir başkası:

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

Ped karakterlerini sabit bir sütun numarasında sonlandırıyorsanız, o zaman üst üste binebilir ve cutuzunluk yapabilirsiniz:

# 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

Otomatik ölçeklendirme / yeniden boyutlandırma Yöntemi ve Örneği ile Basit Konsol Genişliği / Doldurma / Dolgu / Dolgu.

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

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

Şimdi burada, bir anahtarla renkli ve stil formatlı bir dizeyi yazdırmakla ilgili her şeyi yapan tam özellikli bir renkli karakter terminal takımı var.

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

Bir rengi yazdırmak için bu yeterince basit: paint-format "&red;This is %s\n" red Ve daha sonra kalınlaşmak isteyebilirsiniz:paint-format "&bold;%s!\n" WOW

-lSeçeneği paint-formatkonsol yazı tipi ölçülerine işlemleri yapabilirsiniz böylece fonksiyonu metni ölçer.

-vSeçeneği paint-formatfonksiyonu olarak aynı şekilde çalışır printfancak birlikte edilemez-l

Şimdi yayılma için !

paint-span "hello " . " &blue;world" [not: satırsonu terminal dizisi eklemedik, ancak metin terminali doldurur, bu nedenle sonraki satır yalnızca yeni satır terminal dizisi olarak görünür]

ve bunun çıktısı:

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


0

Bash + seq parametre genişletmeye izin vermek için

@Dennis Williamson cevabına benzer şekilde, ancak mevcutsa seq, ped ipinin uzunluğunun sabit kodlanması gerekmez. Aşağıdaki kod, bir değişkeni betiğe konum parametresi olarak aktarmaya izin verir:

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

Kabuğun bunu bir komut bayrağı olarak yorumlamasını önlemek için "-" karakteri yerine ASCII kodu "2D" kullanılır. Diğer bir seçenek ise "=" kullanmak için "3D" dir.

Bağımsız değişken olarak herhangi bir dolgu uzunluğu aktarılmadığında, yukarıdaki kod varsayılan olarak 80 karakterlik standart terminal genişliğindedir.

Bash kabuk değişkeninden COLUMNS(yani mevcut uçbirimin genişliğinden) yararlanmak için, ortam değişkeninin betikte mevcut olması gerekir. Bunun bir yolu, komut dosyasını .("nokta" komutu) ile çalıştırarak tüm ortam değişkenlerini kaynaklamaktır , örneğin:

. /path/to/script

veya (daha iyisi) COLUMNS, çalıştırılırken değişkeni şu şekilde açıkça iletin:

/path/to/script $COLUMNS
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.