如何找到 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)

但可读性较差,我更喜欢稍微长一点的脚本,其中识别特殊操作并将其封装在一个函数中,稍后我可以复制该函数以在其他脚本中使用。

相关内容