printfを使用してテキストを中央に配置する

printfを使用してテキストを中央に配置する
printf "%*s\n" $(((${#fname}+$COLUMNS)/2)) "$fname"

次のエラーが発生します。

line 9: (7+)/2: syntax error: operand expected (error token is ")/2")

これは端末では機能しますが、私のスクリプトでは機能しません。どんな考えがありますか?

答え1

すべてのシェルが$COLUMNS変数を端末幅に設定するわけではありません。

bash5.0より前のバージョンでは、スクリプトではなくインタラクティブにのみ設定されていました。ただし、バージョン4.3以降は、非対話型シェルで引き続き使用できますshopt -s checkwinsize

しかし、どちらの場合も反転があります。非対話型シェルでオプションを有効にすると(5.0以降はデフォルトで有効になっています)、$COLUMNS子プロセスが待って終了するまで/は設定されません(ソースに記載されている項目)。$LINESNEWSフォアグラウンドジョブが終了した後、非対話型シェルには基本的に作業制御がないため、多少誤解を招くおそれがあります。したがって、これらの変数を使用する前に、外部コマンドまたはサブシェルが同期的に実行されることを確認する必要があります。

#! /bin/bash -
shopt -s checkwinsize # for versions 4.3 and 4.4
(:) # start a synchronous subshell that runs the null command
echo "$COLUMNS $LINE"

また、これはstderrが端末に移動したときにのみ発生するため(そうでない場合は設定されていないまま)、画面の幅を決定できない場合は$COLUMNSより合理的なデフォルトを使用することをお勧めします。${COLUMNS:-80}bash

または、端末で実行している場合は非対話型であってもzsh常に設定を切り替えるか(それ以外の場合はデフォルト値は80)、Bourneに似たシェルでif出力を使用して以前に設定した場合に設定できます。設定されていないか空です。$COLUMNS$COLUMNS${COLUMNS:=$(tput cols)}$COLUMNS$COLUMNStput cols

tput colsシステムで機能しない場合は試してみてください。</dev/tty stty size | awk '{print $2}'またはzsh -c 'print $COLUMNS'

ただし、この方法で設定すると端末のサイズが変更されるたびに更新されないため、スクリプトから中央揃えのテキストを印刷するたびに端末のサイズが照会されるように常に代わりに$COLUMNS使用することをお勧めします。$(tput cols)

また参考にしてくださいprintf '%*s'zshシェルの外からfish与えられた量だけテキストを埋め、バイトいいえ数値したがって、この方法は、UTF-8を使用するロケールでUS-ASCII文字(すべての可能な文字の0.011%)に制限された単一バイト、単一幅文字を含むテキストを埋めるためにのみ使用できます。

zsh代わりにを使用している場合は、eftパラメータとightパディングパラメータを使用してbash拡張フラグを使用できます(このフラグを使用して、幅が0または幅が2つの文字を処理することもできます)。lrm

print -r -- ${(ml[COLUMNS/2]r[COLUMNS-COLUMNS/2])fname}

左右(つまり画面の右端)を埋めることに注意してください。以下を使用して、右側のパディング(および末尾のスペース)を削除できます。

set -o extendedglob
print -r -- ${${(ml[COLUMNS/2]r[COLUMNS-COLUMNS/2])fname}%%[[:space:]]#}

色付け/太字/強調表示...エスケープシーケンスを含む中央テキストはより複雑です。最も簡単な方法は、おそらく文字列の幅を取得する前に削除することです。例えばzshその方法文字列の幅を決定し、幅が0または幅が2つの文字を処理します。

varwidth() (( ${(P)#1} * 3 - ${#${(ml[${(P)#1} * 2])${(P)1}}} ))
functions -Ms varwidth

varwidth_without_formatting() {
  set -o localoptions -o extendedglob
  local without_formatting=${(P)1//$'\e'\[[0-9;]#m}
  (( varwidth(without_formatting) ))
}
functions -Ms varwidth_without_formatting

center() {
  local text
  for text do
    print -r -- ${(l[(COLUMNS-varwidth_without_formatting(text))/2])}$text
  done
}

center $'\e[31mred\e[1;39mbold\e[m' \
       ${(%):-%F{green}Blah%F{yellow}blah%F{magenta}blah%f}

1ほとんどのシステムでは、@zevzekがコメントに示すようにSIGWINCHシグナル用のハンドラをインストールできますが、これは最も一般的な場合に役立ちます。

答え2

COLUMNS変数の代わりに外部ソースから値を取得できます。

tput cols 

stty size | cut '-d ' -f1

答え3

以下を試してください。

read WindowHeight WindowWidth<<<$(stty size)
printf "%$(((${#fname}+${WindowWidth})/2))s" "$fname"

COLUMNSはスクリプトで自動的に設定されないため、最良のオプションはsttyを使用して現在のウィンドウサイズを取得することです。これは複数のシェル(bash、ksh、zshを含む)で機能します。

関連情報