¿Cómo puedo encontrar la longitud de la cadena del número de índice de una matriz bash?

¿Cómo puedo encontrar la longitud de la cadena del número de índice de una matriz bash?

En bash, supongamos que alguna función genera una matriz simple de longitud desconocida. La matriz se transformará e imprimirá en la pantalla: cada línea con su pequeño número de índice al lado para elegir. El espacio es reducido, la presentación debe ser impecable, el conjunto de herramientas es... um, sin lujos, diría yo.

¿Cómo puedo saber ellongitud de la cuerdadenúmero de índice en sí(para fines de formato) que no es un valor en la matriz. Por ejemplo, si la matriz tuviera 334,345 elementos (cuyo último elemento imprimiría echo ${hugearray[334344]}), entonces 6sería lo que estoy buscando.

Tengo que permanecer dentro de bash porque no hay tput ni otras ncursesutilidades de formato "elegantes" en el sistema de destino. Sólo golpearse a sí mismo.

${#WORD[@]}Me da el total de elementos, a partir de ahí creé una construcción rudimentaria para obtenerlo:

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

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

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

Funciona pero no es precisamente elegante.En mi defensa, no soy desarrollador.pero esperaba que ya hubiera una manera mejor y más sencilla de hacerlo, indirecciones anidadas o algún otro bash vudú. Pero elcosas normalesque actúa sobre los datos reales ya era bastante confuso, pensé que sería mejor preguntar antes de arrepentirme de algo (el sistema de destino SÓLO tiene la cuenta raíz). ¿Está ahí?

¡Gracias!

Respuesta1

Si estás en macOS, deberías tener acceso a zsh, por lo que no deberías tener que usar bash.

En zsh, la longitud de la matriz es como $#arrayen csh y puede anidar operadores de expansión de parámetros, por lo que puede hacer ${#${#array}}¹ para obtener la longitud de la representación decimal de la longitud de la matriz.

$ 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

Tenga en cuenta que bash ha copiado todo array=(...), array+=(...), desde zsh (a menudo indirectamente a través de ksh). funciona en zsh o ksh pero no en bash.{x..y}{1..$expansion}

El ${a:^b}operador de compresión de matrices y () {code} argslas funciones anónimas siguen siendo específicas de zsh.


Si tiene que usar bash, hacerlo en dos pasos como lo hace probablemente sea lo mejor posible.

Podría hacerlo en una expansión con $(((l=${#array[@]}),SHLVL[l=\${#l}],l)), basándose en la evaluación retrasada de los índices de la matriz, pero eso no ayuda con la legibilidad y puede que no esté preparado para el futuro.

También tenga en cuenta que en bash (como en ksh cuyo diseño de matriz copió desafortunadamente), las matrices no son realmente matrices, sino más bien matrices asociativas con claves que son números enteros positivos y comienzan en 0 (No es uno como en la mayoría de los otros caparazones.)

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

3es el número de elementos de la matriz, pero no el valor clave más alto que necesita:

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

Entonces, si desea imprimir claves y valores alineados, necesita algo como

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

(no funcionará con el antiguo bash que se encuentra en macos; local -nnecesita bash 4.3 o posterior)


¹ ${#$#array}no funciona ya que se toma como ${#${$#array}}, la longitud $$después de haber sido eliminado arraydesde su inicio utilizando el ${var#pattern}operador estilo Korn.

Respuesta2

¿Sería esto una especie de vudú BASH, como lo llamaste? - no hay variables adicionales
: $((${#pkglist[*]}-1)); echo ${#_}; , pero $_ puede provocar problemas de depuración - scripts largos

Respuesta3

EDITAR:Pido disculpas; Como me recordó amablemente @ilkkachu, la forma más sencilla de obtener la longitud de una variable es con ${#variable}, sin utilizar herramientas externas. Por favor ignore esta respuesta; Lo eliminaré en breve. ¡Me avergüenza!

Para determinar cuántos dígitos tiene un número, puedes usar el logaritmo de base 10 del número más uno y luego eliminar la parte decimal, pero la calculadora binaria bctiene una función útil para hacerlo:

echo "length(334344)" | bc

imprimirá 6 como desee. Si el argumento de lengthno es un número entero, contará el número real de dígitos dados sin la coma decimal (es decir, length(3.4560)dará 5).

Para insertar una variable Bash en el echouso de la expansión de variable Bash dentro de las comillas dobles:

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

Podemos escribir eso como una función para llamar fácilmente:

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

luego utilícelo de la siguiente manera:

lendigits=$(num_digits 334344)

o, con una variable

x=334344
lendigits=$(num_digits $x)

Nota:Si bcaún no está instalado en su distribución de Linux, puede agregarlo usando el administrador de paquetes de la distribución.

Nota 2:Por supuesto que puedes usar la expresión directamente sin una función:

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

pero es menos legible y prefiero un script un poco más largo donde las operaciones especiales se identifican y encapsulan en una función que luego puedo copiar para usar en otros scripts.

información relacionada