प्रिंटफ में वर्णों को बाँधना


107

मैं यह दिखाने के लिए बैश शेल स्क्रिप्ट लिख रहा हूं कि कोई प्रक्रिया चल रही है या नहीं।

अब तक, मुझे यह मिला:

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

कोड मुझे यह आउटपुट देता है:

JBoss                                              [DOWN]

GlassFish                                          [UP]

verylongprocessname                                [UP]

मैं इसे और अधिक पठनीय बनाने के लिए '-' या '*' के साथ दो क्षेत्रों के बीच की खाई को पाटना चाहता हूं। खेतों के संरेखण को परेशान किए बिना मैं ऐसा कैसे करूं?

मुझे जो आउटपुट चाहिए वह है:

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

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

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

जवाबों:


77

शुद्ध बैश, कोई बाहरी उपयोगिताओं नहीं

यह प्रदर्शन पूर्ण औचित्य करता है, लेकिन अगर आप दांतेदार-सही लाइनों चाहते हैं तो आप दूसरी स्ट्रिंग की लंबाई घटा सकते हैं।

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"

1
क्या आप थोड़ा सा प्रिंटफ '% *। * S' ... भाग समझा सकते हैं?
atdouard लोपेज

3
@EdouardLopez: तर्क सूची में पहले तारांकन को शून्य से बदल दिया जाता है। दूसरे तर्क में गणना के परिणाम से दूसरा तारांकन प्रतिस्थापित किया जाता है। उदाहरण के लिए, स्ट्रिंग्स "आआआ" और "बब्बब" के लिए परिणाम है '%0.31s'। स्ट्रिंग (अंतिम तर्क) को डॉट के बाद निर्दिष्ट लंबाई तक काट दिया जाता है। शून्य किसी भी स्पेस पैडिंग को आउटपुट होने से रोकता है। इसलिए 31 हाइफ़न आउटपुट हैं।
अगली सूचना तक रोक दिया गया।

1
यह पृष्ठ @Dennis विलियमसन के उत्तर को समझने में मदद कर सकता है: wiki.bash-hackers.org/commands/builtin/printf#modifiers
Lopez

"६.६०} की आवश्यकता ६० चर के रूप में; ... जैसे" वर = ६० "
रीगन मिरांडा

@ReeganMiranda: यह तकनीक जिस तरह से काम करती है वह यह है कि आप जिस मूल्य की सबसे बड़ी जरूरत है उसका मूल्य कोड करते हैं और padlengthआउटपुट के लिए वास्तविक लंबाई का चयन करते हैं।
अगली सूचना तक रोक दिया गया।

68

शुद्ध बैश। निर्धारित स्ट्रिंग 'लाइन' के लिए ऑफसेट के रूप में '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]

जादू $ {लाइन: $ {# PROC_NAME}} है, जो चर रेखा के एक बिंदु से केवल वापसी शुरू करने के लिए बैश विकल्प का उपयोग करता है, जो कि PROC_NAME में वर्णों की संख्या पर शुरू होने के लिए निर्धारित है। tldp.org/LDP/abs/html/string-manipulation.html#SUBSTREXTR01
cwingrav

ध्यान दें कि यह उस मामले को नहीं संभालता है जहां PROC_NAMEरिक्त स्थान है जब तक कि वे पहले से ही बच नहीं गए हैं। आपको प्रत्येक चर में दो-अलग-अलग-अलग-अलग टोकन के लिए दो टोकन के साथ एक लाइन मिलेगी और फिर [यूपी] आपके lineटेक्स्ट स्ट्रिंग की कुल लंबाई शून्य से अंत में एक पंक्ति । इसलिए सावधान रहें, क्योंकि यह जटिल स्क्रिप्ट में किए जाने पर दिलचस्प और संभावित असुरक्षित बग पैदा कर सकता है। अन्यथा छोटा और सरल। :)
dodexahedron

19

तुच्छ (लेकिन काम) समाधान:

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

4
लेकिन केवल एक टर्मिनल पर। यदि आउटपुट किसी फ़ाइल में भेजा जाता है तो यह गड़बड़ हो जाएगा।
13

5
तो क्या आप वास्तव में एक तुच्छ समाधान से उम्मीद करते हैं?!? पूर्ण काम भी आउटपुट पुनर्निर्देशन के साथ?!? ]: पी
निकोला लियोनी

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 डैश बनाएं
    (डैश को विकल्प के रूप में व्याख्या किया गया है, इसलिए इसके बजाय एससीआई कोड का उपयोग करें)
  • "$PROC_NAME ..." - $ PROC_NAME को संक्षिप्त करें और डैश करें
  • | head -c 40 - पहले 40 वर्णों के लिए ट्रिम स्ट्रिंग

अजीब बात है, जब मैं ऐसा printf 'x' {1..40}करता हूं तो केवल सिंगल xहम्म प्रिंट करता है
क्रिस्चियन

@ क्रिस्टियन इसलिए कि आपने प्रारूप की प्रतिलिपि नहीं बनाई है: 'प्रिंटफ' x% .0s '{1..40} `प्रिंट्स 40 xs
आर्टम

विकल्प के रूप में व्याख्या किए जा रहे डैश से बचने के लिए डबल डैश का उपयोग यह संकेत देने के लिए किया जा सकता है कि बाकी गैर-विकल्प तर्क हैं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

उत्पादन करने के लिए stdout:

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

4

echoकेवल उपयोग करना

@ डेनिस विलियमसन की awser सिर्फ ठीक काम कर रही है सिवाय इसके कि मैं इको का उपयोग करके ऐसा करने की कोशिश कर रहा था। इको एक निश्चित रंग के साथ चार्जर्स को आउटपुट करने की अनुमति देता है। प्रिंटफ़ का उपयोग करने से उस रंग को हटा दिया जाएगा और अपठनीय वर्णों को प्रिंट किया जा सकेगा। यहाँ echo-only विकल्प है:

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

उत्पादन:

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

निश्चित रूप से आप @ डेनिस विलियम्सन द्वारा प्रस्तावित सभी विविधताओं का उपयोग कर सकते हैं, चाहे आप सही भाग को छोड़ दिया जाना चाहते हैं या दाएं-संरेखित ( आदि 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

बैश + seq पैरामीटर विस्तार की अनुमति देने के लिए

@ डेनिस विलियमसन के जवाब के समान, लेकिन अगर 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" का उपयोग चरित्र के बजाय "-" किया जाता है ताकि शेल को कमांड फ्लैग के रूप में व्याख्या करने से बचा जा सके। एक और विकल्प "=" का उपयोग करने के लिए "3 डी" है।

तर्क के रूप में पारित किए गए किसी भी पेडलेंथ की अनुपस्थिति में, ऊपर का कोड 80 वर्ण मानक टर्मिनल चौड़ाई के लिए चूक है।

बैश शेल वैरिएबल COLUMNS(यानी, वर्तमान टर्मिनल की चौड़ाई) का लाभ उठाने के लिए , पर्यावरण चर को स्क्रिप्ट के लिए उपलब्ध होना चाहिए। एक तरीका यह है .कि इस तरह से ("डॉट" कमांड) से पहले स्क्रिप्ट को निष्पादित करके सभी पर्यावरण चर को स्रोत किया जाए :

. /path/to/script

या (बेहतर) COLUMNSनिष्पादित करते समय स्पष्ट रूप से चर को पास करें , जैसे:

/path/to/script $COLUMNS
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.