printfの文字の埋め込み


107

プロセスが実行されているかどうかを表示するbashシェルスクリプトを書いています。

これまでのところ、私はこれを手に入れました:

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

コードは私にこの出力を与えます:

JBoss                                              [DOWN]

GlassFish                                          [UP]

verylongprocessname                                [UP]

2つのフィールド間のギャップを「-」または「*」で埋めて、読みやすくします。フィールドの配置を乱すことなくそれを行うにはどうすればよいですか?

私が欲しい出力は:

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

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

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

回答:


77

Pure Bash、外部ユーティリティなし

このデモは完全な正当化を行いますが、不揃いの右の線が必要な場合は、2番目の文字列の長さを引くことを省略できます。

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

残念ながら、その手法では、パッド文字列の長さは、必要と思われる最も長いものよりも長くなるようにハードコード化する必要がありますが、パッド長は示されているように変数にすることができます。ただし、パッドの長さの変数を使用できるように、最初の行をこれらの3つで置き換えることができます。

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

したがって、パッド(padlimitおよびpadlength)は、端子幅($COLUMNS)に基づくか、最長のデータ文字列の長さから計算できます。

出力:

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

2番目の文字列の長さを差し引かずに:

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

最初の行は代わりに同等のものにすることができます(に似ていますsprintf):

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

または同様に、より動的な手法の場合:

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

必要に応じて、1行ですべて印刷できます。

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

1
printf '%*。* s' ...の部分について少し説明していただけますか?
エドゥアールロペス

3
@EdouardLopez:最初のアスタリスクは、引数リストでゼロに置き換えられます。2番目のアスタリスクは、2番目の引数の計算結果に置き換えられます。たとえば、文字列 "aaaa"と "bbbbb"の結果は'%0.31s'です。文字列(最後の引数)は、ドットの後に指定された長さに切り捨てられます。ゼロは、スペースのパディングが出力されないようにします。したがって、31個のハイフンが出力されます。
追って通知があるまで一時停止。

1
このページには、@Dennisウィリアムソンの答えを理解するのに役立つことができます:wiki.bash-hackers.org/commands/builtin/printf#modifiers
エドゥアール・ロペス

{1..60}は変数として60を必要としています; ... "var = 60"のように
Reegan Miranda '19

@ReeganMiranda:この手法が機能する方法は、値を必要な最大値にハードコーディングし、それを使用padlengthして出力する実際の長さを選択することです。
追って通知があるまで一時停止。

68

ピュアバッシュ。「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]

魔法は$ {line:$ {#PROC_NAME}}です。これは、bashサブストリング抽出を使用して、PROC_NAMEの文字数で開始するように設定されている変数行のポイントからのみ戻りを開始します。tldp.org/LDP/abs/html/string-manipulation.html#SUBSTREXTR01
cwingrav

PROC_NAMEすでにエスケープされていない限り、これはスペースがある場合を処理しないことに注意してください。変数内のスペースで区切られた2つのトークンごとに[UP]の2つのトークンを1行取得し、最後にlineテキストから入力文字列の全長を引いた1行を取得します。複雑なスクリプトで実行すると、興味深いバグが発生する可能性があるため、注意してください。それ以外の場合は短くて簡単です。:)
dodexahedron 2018

19

ささいな(しかし機能する)ソリューション:

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

4
しかし、端末上でのみ。出力がファイルに送信されると、混乱を招きます。
thkala

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}それを実行すると、1 つのxhmmm しか印刷されない
Krystian

@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

stdoutへの出力:

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

4

echoのみ使用

@Dennis Williamsonのanwserは、エコーを使用してこれを実行しようとしたことを除いて、問題なく動作しています。エコーは、特定の色で文字を出力することができます。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(つまり、現在の端末の幅)を利用するには、スクリプトで環境変数を使用できる必要があります。1つの方法は.、次のように(「ドット」コマンド)が前に付いたスクリプトを実行して、すべての環境変数を取得することです。

. /path/to/script

または(より良い)次のCOLUMNSように、実行時に変数を明示的に渡します。

/path/to/script $COLUMNS
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.