![Como posso encontrar o comprimento da string do número de índice de uma matriz bash?](https://rvso.com/image/231058/Como%20posso%20encontrar%20o%20comprimento%20da%20string%20do%20n%C3%BAmero%20de%20%C3%ADndice%20de%20uma%20matriz%20bash%3F.png)
No bash, suponha que alguma função gere um array simples de comprimento desconhecido. O array será transformado e impresso na tela: cada linha com seu pequeno número de índice próximo a ela para escolher. O espaço é apertado, a apresentação deve ser impecável, o conjunto de ferramentas é... hum, sem frescuras, eu diria.
Como posso descobrir ocomprimento da cordadepróprio número de índice(para fins de formatação) que não é um valor na matriz. Por exemplo, se a matriz tivesse 334.345 itens (cujo último item você imprimiria echo ${hugearray[334344]}
), então 6
seria o que estou procurando.
Eu tenho que ficar no bash porque não há tput ou outros ncurses
utilitários de formatação "sofisticados" no sistema de destino. Apenas bata em si mesmo.
${#WORD[@]}
Me dá o total de itens, a partir disso criei uma construção rudimentar para obtê-lo:
$〉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, mas não é exatamente elegante.Em minha defesa, não sou desenvolvedormas eu esperava que já existisse uma maneira melhor e mais simples de fazer isso, indiretas aninhadas ou algum outro vodu. Mas ocoisas normaisque atua sobre os dados reais já era bastante confuso, pensei que seria melhor perguntar antes de se arrepender de algo (o sistema de destino SOMENTE tem a conta root). Existe?
Obrigado!
Responder1
Se você estiver no macOS, deverá ter acesso ao zsh, portanto não precisará usar o bash.
No zsh, o comprimento do array é igual $#array
ao csh e você pode aninhar operadores de expansão de parâmetros, então você pode fazer ${#${#array}}
¹ para obter o comprimento da representação decimal do comprimento do array.
$ 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
Observe que todos array=(...)
, array+=(...)
, {x..y}
foram copiados pelo bash do zsh (geralmente indiretamente via ksh). {1..$expansion}
funciona em zsh ou ksh, mas não em bash.
O ${a:^b}
operador de compactação de array e () {code} args
as funções anônimas ainda são específicos do zsh.
Se você tiver que usar o bash, fazê-lo em duas etapas provavelmente será o melhor possível.
Você poderia fazer isso em uma expansão com $(((l=${#array[@]}),SHLVL[l=\${#l}],l))
, contando com a avaliação atrasada dos índices da matriz, mas isso não ajuda na legibilidade e pode não ser à prova de futuro.
Observe também que no bash (como no ksh, cujo design de array o bash copiou infelizmente), os arrays não são realmente arrays, mas mais como arrays associativos com chaves sendo números inteiros positivos e começando em 0 (não 1 como na maioria dos outros shells)
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
é o número de elementos na matriz, mas não o valor de chave mais alto necessário:
bash-5.1$ keys=("${!a[@]}")
bash-5.1$ echo "${keys[@]: -1}"
4567
Então, se você quiser imprimir chaves e valores alinhados, você precisa de 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
(não funciona com o bash antigo encontrado no macos; local -n
precisa do bash 4.3 ou mais recente)
¹ ${#$#array}
não funciona porque é considerado ${#${$#array}}
, o comprimento $$
após ter sido removido array
desde o início usando o ${var#pattern}
operador estilo Korn.
Responder2
Isso seria algum tipo de vodu BASH, como você o chamou? - nenhuma variável adicional
: $((${#pkglist[*]}-1)); echo ${#_};
, mas $_ pode levar a problemas de depuração - scripts longos
Responder3
EDITAR:Peço desculpas; como @ilkkachu gentilmente me lembrou, a maneira mais simples de obter o comprimento de uma variável é ${#variable}
sem usar ferramentas externas. Por favor, ignore esta resposta; Vou excluí-lo em breve. Que vergonha!
Para determinar quantos dígitos tem um número, você pode usar o logaritmo da base 10 do número mais um e depois eliminar a parte decimal, mas a calculadora binária bc
tem uma função útil para fazer isso:
echo "length(334344)" | bc
imprimirá 6 conforme desejado. Se o argumento de length
não for um número inteiro, ele contará o número real de dígitos fornecidos sem a vírgula decimal (ou seja, length(3.4560)
dará 5).
Para inserir uma variável Bash no echo
uso da expansão da variável Bash entre aspas duplas:
x=334344
echo "length($x)" | bc
Podemos escrever isso como uma função para facilitar a chamada:
function num_digits {
#determine hoy many digits has a number
#param number
local number="$1"
echo "length($number)" | bc
}
então use-o da seguinte maneira:
lendigits=$(num_digits 334344)
ou, com uma variável
x=334344
lendigits=$(num_digits $x)
Observação:Se bc
ainda não estiver instalado em sua distribuição Linux, você pode adicioná-lo usando o gerenciador de pacotes da distribuição.
Nota 2:Claro que você pode usar a expressão diretamente sem uma função:
x=334344
lendigits=$(echo "length($x)" | bc)
mas é menos legível e prefiro um script um pouco mais longo onde as operações especiais são identificadas e encapsuladas em uma função que posteriormente posso copiar para usar em outros scripts.