bash 配列のインデックス番号の文字列の長さを調べるにはどうすればよいでしょうか?

bash 配列のインデックス番号の文字列の長さを調べるにはどうすればよいでしょうか?

bash で、ある関数が長さが不明な単純な配列を生成するとします。配列は変換されて画面に出力されます。各行の横には選択するための小さなインデックス番号が表示されます。スペースは限られており、プレゼンテーションは完璧でなければなりません。ツールセットは… うーん、飾り気のない、と言えるでしょう。

どうすれば文字列の長さインデックス番号自体(フォーマット目的のため) 配列上の値ではありません。たとえば、配列に 334,345 個の項目がある場合 (最後の項目は で印刷されますecho ${hugearray[334344]})、6が探しているものになります。

ncursesターゲット システムには tput やその他の「派手な」フォーマット ユーティリティがないため、bash 内にとどまる必要があります。bash 自体のみです。

${#WORD[@]}アイテムの合計数が表示されます。そこから、それを取得するための基本的な構造を作成しました。

$〉pkglist+=($(brew list -1)) # ← I'm testing on a Mac.
$〉printf "%s\n" ${pkglist[@]}
automake
xquartz

$〉echo ${#pkglist[@]}
68

$〉indexlength=${#pkglist[@]}
$〉echo ${#indexlength}
2

機能しますが、エレガントとは言えません。弁解すると、私は開発者ではないしかし、ネストされた間接参照やその他のbashの魔法など、もっと簡単な方法がすでにあるのではないかと期待していました。しかし、普通のもの実際のデータに作用するものは、それだけでも十分に混乱を招くので、後悔する前に質問するのが最善だと思いました (ターゲット システムにはルート アカウントのみがあります)。ありますか?

ありがとう!

答え1

macOS を使用している場合は、zsh にアクセスできるはずなので、bash を使用する必要はありません。

zsh では、配列の長さは$#arraycsh と同じで、パラメータ拡張演算子をネストできるため、${#${#array}}¹ を実行して配列の長さの 10 進表現の長さを取得できます。

$ a=(qwe asdasd qewqw)
$ () {printf "%${#${#a}}d %s\n" "${@:^a}"} {1..$#a}
1 qwe
2 asdasd
3 qweqe
$ a=( {a..m} )
$ () {printf "%${#${#a}}d %s\n" "${@:^a}}" {1..$#a}
 1 a
 2 b
 3 c
 4 d
 5 e
 6 f
 7 g
 8 h
 9 i
10 j
11 k
12 l
13 m

array=(...)、、はすべてarray+=(...){x..y}bash によって zsh からコピーされていることに注意してください (多くの場合、ksh を介して間接的に)。zsh{1..$expansion}または ksh では機能しますが、bash では機能しません。

配列${a:^b}圧縮演算子と() {code} args匿名関数は、依然として zsh に固有のものです。


bash を使用する必要がある場合は、あなたが行っているように 2 つの手順で実行するのがおそらく最善の方法です。

配列インデックスの遅延評価に依存して、を使用した 1 回の展開でこれを行うこともできます$(((l=${#array[@]}),SHLVL[l=\${#l}],l))が、可読性は向上せず、将来性がない可能性があります。

また、bashでは(残念ながらbashが配列設計をコピーしたkshと同様に)、配列は実際には配列ではなく、キーが正の整数で0から始まる連想配列に似ていることに注意してください(他のほとんどのシェルのように1ではない

bash-5.1$ a=( [5]=123 [123]=qwe [4567]=asd )
bash-5.1$ typeset -p a
declare -a a=([5]="123" [123]="qwe" [4567]="asd")
bash-5.1$ echo "${#a[@]}"
3

3配列内の要素数ですが、必要な最高キー値ではありません。

bash-5.1$ keys=("${!a[@]}")
bash-5.1$ echo "${keys[@]: -1}"
4567

したがって、キーと値を揃えて印刷したい場合は、次のようなものが必要です。

printarray() {
  local -n arr="$1"
  local -a keys=("${!arr[@]}")
  local highest_key="${keys[@]: -1}"
  local key_width="${#highest_key}"
  local key
  for key in "${keys[@]}"; do
    printf '%*d %s\n' "$key_width" "$key" "${arr[key]}"
  done
}
bash-5.1$ printarray a
   5 123
 123 qwe
4567 asd

(macOS の古い bash では動作しません。bash local -n4.3 以降が必要です)


¹ は、Korn スタイルの演算子を使用して先頭からが削除された後の長さ${#$#array}として解釈されるため、機能しません。${#${$#array}}$$array${var#pattern}

答え2

これは、あなたが言うように、ある種の BASH の呪術でしょうか? - 追加の変数はありません
: $((${#pkglist[*]}-1)); echo ${#_}; が、$_ はデバッグの問題を引き起こす可能性があります - 長いスクリプト

答え3

編集:申し訳ありません。@ilkkachu が親切に教えてくれたように、変数の長さを取得する最も簡単な方法は${#variable}、外部ツールではなく を使用することです。この回答は無視してください。すぐに削除します。恥ずかしいです!

数字の桁数を決定するには、その数字の 10 を底とする対数に 1 を加えて小数点以下を切り捨てるという方法がありますが、2 進計算機にはbcそれを実行する便利な関数があります。

echo "length(334344)" | bc

は、希望どおりに 6 を出力します。引数がlength整数でない場合は、小数点のコンマを除いた実際の桁数をカウントします (つまり、length(3.4560)5 を返します)。

Bash 変数を挿入するには、echo二重引用符内で Bash 変数展開を使用します。

x=334344
echo "length($x)" | bc

簡単に呼び出せるように関数として記述することができます。

function num_digits {
#determine hoy many digits has a number
#param number
    local number="$1"
    echo "length($number)" | bc
}

次のように使用します。

lendigits=$(num_digits 334344)

または、変数

x=334344
lendigits=$(num_digits $x)

注記:Linux ディストリビューションにまだインストールされていない場合はbc、ディストリビューションのパッケージ マネージャーを使用して追加できます。

注2:もちろん、関数なしで式を直接使用することもできます。

x=334344
lendigits=$(echo "length($x)" | bc)

しかし、読みにくくなるため、特殊な操作を識別して関数にカプセル化し、後でコピーして他のスクリプトで使用できるようにした、少し長いスクリプトの方が好みです。

関連情報