Karakter pengisi di printf

107

Saya menulis skrip bash shell untuk ditampilkan jika suatu proses sedang berjalan atau tidak.

Sejauh ini, saya mendapatkan ini:

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

Kode memberi saya keluaran ini:

JBoss                                              [DOWN]

GlassFish                                          [UP]

verylongprocessname                                [UP]

Saya ingin mengisi jarak antara dua kolom dengan '-' atau '*' agar lebih mudah dibaca. Bagaimana cara melakukannya tanpa mengganggu kesejajaran bidang?

Output yang saya inginkan adalah:

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

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

verylongprocessname -----------------------------  [UP]
ramah
sumber

Jawaban:

77

Pure Bash, tidak ada utilitas eksternal

Demonstrasi ini melakukan justifikasi penuh, tetapi Anda bisa menghilangkan pengurangan panjang string kedua jika Anda menginginkan garis yang tidak rata.

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

Sayangnya, dalam teknik itu, panjang string pad harus di-hardcode agar lebih panjang dari yang terpanjang yang Anda pikir Anda perlukan, tetapi padlength bisa menjadi variabel seperti yang ditunjukkan. Namun, Anda dapat mengganti baris pertama dengan ketiga baris ini agar dapat menggunakan variabel untuk panjang bantalan:

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

Jadi pad ( padlimitdan padlength) bisa didasarkan pada lebar terminal ( $COLUMNS) atau dihitung dari panjang string data terpanjang.

Keluaran:

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

Tanpa mengurangi panjang senar kedua:

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

Baris pertama malah bisa sama (mirip dengan sprintf):

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

atau serupa untuk teknik yang lebih dinamis:

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

Anda dapat melakukan pencetakan semua dalam satu baris jika Anda mau:

printf '%s%*.*s%s\n' "$string1" 0 $((padlength - ${#string1} - ${#string2} )) "$pad" "$string2"
Dijeda sampai pemberitahuan lebih lanjut.
sumber
1
Bisakah Anda menjelaskan sedikit bagian printf '% *. * S' ...?
Édouard Lopez
3
@EdouardLopez: Tanda bintang pertama diganti dengan nol dalam daftar argumen. Tanda bintang kedua diganti dengan hasil kalkulasi pada argumen kedua. Hasilnya, untuk string "aaaa" dan "bbbbb", misalnya, adalah '%0.31s'. String (argumen terakhir) dipotong sesuai panjang yang ditentukan setelah titik. Nol mencegah padding ruang apa pun menjadi keluaran. Jadi 31 tanda hubung adalah keluaran.
Dijeda sampai pemberitahuan lebih lanjut.
1
Halaman ini dapat membantu memahami jawaban @Dennis
Édouard Lopez
{1..60} membutuhkan 60 sebagai variabel; ... seperti "var = 60"
Reegan Miranda
@ReeganMiranda: Cara kerja teknik ini adalah Anda membuat kode keras nilai ke nilai terbesar yang Anda butuhkan dan gunakan padlengthuntuk memilih panjang aktual ke keluaran.
Dijeda sampai pemberitahuan lebih lanjut.
68

Bash Murni. Gunakan panjang nilai 'PROC_NAME' sebagai offset untuk string tetap '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}}"

Ini memberi

abc ------------------------------------- [UP]
abcdef ---------------------------------- [UP]
Fritz G. Mehner
sumber
Ajaibnya adalah $ {baris: $ {# PROC_NAME}}, yang menggunakan ekstraksi substring bash untuk memulai hanya kembali dari titik baris variabel, yang diatur untuk dimulai dari jumlah karakter di PROC_NAME. tldp.org/LDP/abs/html/string-manipulation.html#SUBSTREXTR01
cwingrav
Perhatikan bahwa ini tidak menangani kasus di mana PROC_NAMEmemiliki spasi kecuali mereka sudah di-escape. Anda akan mendapatkan satu baris dengan masing-masing dua token dan kemudian [UP] untuk setiap dua token yang dipisahkan spasi dalam variabel Anda dan kemudian satu baris di akhir dengan lineteks Anda dikurangi total panjang string input Anda. Jadi berhati-hatilah, karena ini dapat menyebabkan bug yang menarik dan berpotensi tidak aman jika dilakukan dalam skrip yang kompleks. Jika tidak pendek dan sederhana. :)
dodexahedron
19

Solusi sepele (tapi berhasil):

echo -e "---------------------------- [UP]\r$PROC_NAME "
Nicola Leoni
sumber
4
Tapi hanya di terminal. Jika output dikirim ke file maka akan berantakan.
thkala
5
jadi apa yang Anda harapkan dari solusi sepele?!? bekerja penuh juga dengan pengalihan keluaran?!? ]: P
Nicola Leoni
14

Saya rasa ini adalah solusi paling sederhana. Bawaan shell murni, tanpa matematika inline. Ini meminjam dari jawaban sebelumnya.

Hanya substring dan meta-variabel $ {# ...}.

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"

Menghasilkan

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

Menghasilkan

-----<] A very long header
---------------<] shrt hdr
synthesizerpatel
sumber
12

Tidak ada cara untuk mengisi apapun kecuali menggunakan spasi printf. Anda dapat menggunakan sed:

printf "%-50s@%s\n" $PROC_NAME [UP] | sed -e 's/ /-/g' -e 's/@/ /' -e 's/-/ /'
F'x
sumber
7
+1 Ada masalah jika PROC_NAME berisi tanda hubung - mudah diselesaikan dengan tambahan @: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]"

Penjelasan:

  • printf '\055%.0s' {1..40}- Buat 40 tanda hubung
    ( tanda hubung diartikan sebagai opsi jadi gunakan kode ascii yang lolos)
  • "$PROC_NAME ..." - Gabungkan $ PROC_NAME dan tanda hubung
  • | head -c 40 - Pangkas string menjadi 40 karakter pertama
draganHR
sumber
Aneh, ketika saya melakukannya printf 'x' {1..40}hanya mencetak satu xhmmm
Krystian
@Krystian itu karena kamu belum menyalin format: `printf 'x% .0s' {1..40}` prints 40 xs
artm
Untuk menghindari tanda hubung ditafsirkan sebagai opsi tanda hubung ganda dapat digunakan untuk memberi sinyal bahwa sisanya adalah argumen non-opsiprintf -- "-%.0s" {1..40}
artm
7

Yang ini bahkan lebih sederhana dan tidak menjalankan perintah eksternal.

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

JBoss............... [UP]
Chad Juliano
sumber
5

Sederhana tetapi berhasil:

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

Contoh penggunaan:

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

Output ke stdout:

JBoss -------------------------------------------- [DOWN]
GlassFish ---------------------------------------- [UP]
VeryLongProcessName ------------------------------ [UP]
Luis Daniel
sumber
4

echohanya menggunakan

Jawaban @Dennis Williamson berfungsi dengan baik kecuali saya mencoba melakukan ini menggunakan echo. Echo memungkinkan untuk mengeluarkan karakter dengan warna tertentu. Menggunakan printf akan menghapus pewarnaan itu dan mencetak karakter yang tidak dapat dibaca. Inilah echoalternatif-satunya:

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

keluaran:

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

tentu saja Anda dapat menggunakan semua variasi yang diusulkan oleh @Dennis Williamson apakah Anda ingin bagian kanan rata kiri atau kanan (diganti 25 - ${#string1}dengan 25 - ${#string1} - ${#string2}dll ...

Chris Maes
sumber
2

Ini satu lagi:

$ { 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]
thkala
sumber
2

Jika Anda mengakhiri karakter pad pada beberapa nomor kolom tetap, maka Anda dapat menimpa dan cutmemanjang:

# 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"
Gurn
sumber
2

Simple Console Span / Fill / Pad / Padding dengan metode penskalaan / pengubahan ukuran otomatis dan Contoh.

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

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

Sekarang di sini adalah suite-terminal-karakter-warna-fitur-fitur lengkap yang melakukan segalanya sehubungan dengan pencetakan warna dan gaya yang diformat string dengan kunci pas.

# Author: Triston J. Taylor <[email protected]>
# 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";
}

Untuk mencetak warna , itu cukup sederhana: paint-format "&red;This is %s\n" red Dan Anda mungkin ingin tampil berani nanti:paint-format "&bold;%s!\n" WOW

The -lpilihan untuk paint-formatfungsi mengukur teks sehingga Anda dapat melakukan operasi metrik huruf konsol.

The -vpilihan untuk paint-formatfungsi bekerja sama dengan printftetapi tidak dapat diberikan dengan-l

Sekarang untuk spanning !

paint-span "hello " . " &blue;world" [catatan: kami tidak menambahkan urutan terminal baris baru, tetapi teks mengisi terminal, jadi baris berikutnya hanya tampak seperti urutan terminal baris baru]

dan outputnya adalah:

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

Sistem Hypersoft
sumber
0

Bash + seq untuk memungkinkan perluasan parameter

Mirip dengan jawaban @Dennis Williamson, tetapi jika seqtersedia, panjang string pad tidak perlu di-hardcode. Kode berikut memungkinkan untuk melewatkan variabel ke skrip sebagai parameter posisi:

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

Kode ASCII "2D" digunakan sebagai pengganti karakter "-" untuk menghindari shell menafsirkannya sebagai tanda perintah. Pilihan lainnya adalah "3D" untuk menggunakan "=".

Jika tidak ada padlength yang diteruskan sebagai argumen, kode di atas secara default menggunakan lebar terminal standar 80 karakter.

Untuk memanfaatkan variabel bash shell COLUMNS(yaitu, lebar terminal saat ini), variabel lingkungan harus tersedia untuk skrip. Salah satu caranya adalah dengan mengambil semua variabel lingkungan dengan menjalankan skrip yang diawali dengan .(perintah "titik"), seperti ini:

. /path/to/script

atau (lebih baik) secara eksplisit meneruskan COLUMNSvariabel saat menjalankan, seperti ini:

/path/to/script $COLUMNS
Loye Young
sumber