如何找到 bash 陣列索引號的字串長度?

如何找到 bash 陣列索引號的字串長度?

在 bash 中,假設某個函數會產生一個未知長度的簡單陣列。該數組將被轉換並印在螢幕上:每行旁邊都有一個小索引號,可以從中進行選擇。空間有限,演示必須完美無缺,工具集是……嗯,沒有多餘的裝飾,我想說。

我怎樣才能找到字串長度索引號碼本身(出於格式化目的)這不是數組上的值。例如,如果數組有 334,345 個項目(您將使用 列印最後一個項目echo ${hugearray[334344]}),那麼這6就是我要查找的內容。

ncurses我必須留在 bash 中,因為目標系統中沒有 tput 或其他「花哨」的格式化實用程式。只打擊本身。

${#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 中,陣列長度就像$#array在 csh 中一樣,您可以嵌套參數擴充運算符,因此您可以執行${#${#array}}1 來取得陣列長度的十進位表示形式的長度。

$ 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 間接複製)。{1..$expansion}在 zsh 或 ksh 中有效,但在 bash 中無效。

數組${a:^b}壓縮運算子和() {code} args匿名函數仍然是 zsh 特有的。


如果您必須使用 bash,那麼像您這樣分兩步驟進行可能就最好了。

您可以在一次擴充中完成此操作$(((l=${#array[@]}),SHLVL[l=\${#l}],l)),依賴陣列索引的延遲評估,但這對易讀性沒有幫助,並且可能無法面向未來。

另請注意,在 bash 中(就像在 ksh 中,不幸的是,bash 的數組設計被複製了),數組並不是真正的數組,而是更像關聯數組,鍵為正整數並從 0 開始(與大多數其他 shell 不同,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;local -n需要 bash 4.3 或更高版本)


¹${#$#array}不起作用,因為它被視為,即使用Korn 風格運算符從其開頭剝離後${#${$#array}}的長度。$$array${var#pattern}

答案2

正如您所說,這會是某種 BASH 巫術嗎? - 沒有額外的變量
: $((${#pkglist[*]}-1)); echo ${#_}; ,但 $_ 可能會導致調試問題 - 長腳本

答案3

編輯:我道歉;正如 @ilkkachu 善意提醒我的那樣,獲取變數長度的最簡單方法是使用${#variable},而不是使用外部工具。請忽略這個答案;我很快就會刪除它。我真丟臉!

要確定數字有多少位,您可以使用該數字以 10 為底的對數加一,然後去掉小數部分,但二進制計算器bc有一個方便的函數可以做到這一點:

echo "length(334344)" | bc

將根據需要列印 6。如果 的參數length不是整數,它將計算不含小數逗號的實際位數(即,length(3.4560)將給出 5)。

echo要在雙引號內使用Bash 變數擴充插入 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)

筆記:如果bc您的 Linux 發行版中尚未安裝,您可以使用發行版的套件管理器來新增它。

筆記2:當然,您可以直接使用表達式而不使用函數:

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

但可讀性較差,我更喜歡稍微長一點的腳本,其中識別特殊操作並將其封裝在一個函數中,稍後我可以複製該函數以在其他腳本中使用。

相關內容