Existe uma maneira de ler o último elemento de uma matriz com bash?

68

Se eu tiver uma matriz com 5 elementos, por exemplo:

[a][b][c][d][e]

Usando echo ${myarray[4]}eu posso ver o que ele contém.

Mas e se eu não soubesse o número de elementos em uma determinada matriz? Existe uma maneira de ler o último elemento de uma matriz de comprimento desconhecido? ou seja, o primeiro elemento lendo da direita para a esquerda para qualquer array?

Eu gostaria de saber como fazer isso no bash.

3kstc
fonte
$@não é exatamente uma matriz (não pode ser subscrito). Para isso, consulte Obtendo o último argumento passado para um shell script .
Tom Hale

Respostas:

89

Você pode apenas usar um índice negativo ${myarray[-1]} para obter o último elemento. Você pode fazer o mesmo pelo penúltimo e assim por diante; em Bash:

Se o subscrito usado para referenciar um elemento de uma matriz indexada for avaliado como um número menor que zero, ele será interpretado como relativo a um maior que o índice máximo da matriz, para que os índices negativos sejam contados a partir do final da matriz e um índice de -1 refere-se ao último elemento.

O mesmo também funciona para atribuição. Quando diz "expressão", realmente significa uma expressão; você pode escrever em qualquer expressão aritmética existente para calcular o índice, incluindo uma que calcule usando ${#myarray[@]}explicitamente o comprimento da matriz .

Michael Homer
fonte
2
Você pode fazer isso em kshe zshbem.
Janis
5
No zshentanto, por padrão, as matrizes são indexadas 1, ao contrário bashe kshonde são indexadas 0.
Stephen Kitt
2
Sim, claro; a resposta curta a essa pergunta não muda, mas, como a forma longa foi mencionada, achei necessário apontar a diferença de comportamento.
Stephen Kitt
22
O índice negativo funciona apenas no bash 4.3 e acima.
precisa saber é
10
A versão do Bash incluída no Mac OS X a partir da versão 10.11.5 é de apenas 3.2, portanto, isso não funciona em Macs.
Doktor J
45

Modern bash (v4.1 ou melhor)

Você pode ler o último elemento no índice -1:

$ a=(a b c d e f)
$ echo ${a[-1]}
f

Suporte para acessar matrizes indexadas numericamente a partir do final usando índices negativos iniciados com a versão 4.1-alpha do bash .

Bash mais antigo (v4.0 ou anterior)

Você deve obter o comprimento da matriz ${#a[@]}e subtrair um para obter o último elemento:

$ echo ${a[${#a[@]}-1]}
f

Como o bash trata os subscritos da matriz como uma expressão aritmética, não há necessidade de notação adicional, como $((...))forçar a avaliação aritmética.

John1024
fonte
o último não funciona para mim; Estou usando o Bash v4.1.2 (1): em vez de imprimir o último item, ele apenas imprime toda a matriz.
Alexej Magura
A resposta de @ cuonglm funciona, no entanto.
Alexej Magura #
A resposta seria ainda melhor se você pudesse se qualificar moderncom uma versão.
Samveen
11
Exatamente o que era necessário para tornar a resposta perfeita.
Samveen
11
Obrigado por isso. Eu estava usando echo $ {a [$ (($ {# a [@]} - 1]))} porque não sabia sobre "o bash trata os subscritos da matriz como uma expressão aritmética".
precisa saber é o seguinte
15

bashatribuição de matriz, referência, configuração com índice negativo foram adicionadas apenas no bash 4.3 . Com a versão anterior bash, você pode usar expressão no índicearray[${#array[@]-1}]

Outra maneira, também trabalhe com a versão mais antiga do bash(bash 3.0 ou melhor):

$ a=([a] [b] [c] [d] [e])
$ printf %s\\n "${a[@]:(-1)}"
[e]

ou:

$ printf %s\\n "${a[@]: -1}"
[e]

Usando o deslocamento negativo, é necessário separar :com -para evitar ser confundido com a :-expansão.

cuonglm
fonte
11
Faça isso "${a[@]: -1}"e ele funcionará (além de ) bashe zshtambém dentro ksh.
Janis
Os documentos do Kornshell ( www2.research.att.com/sw/download/man/man1/ksh.html ) especificam-no completamente. (Não ter inspecionado os documentos de zshou bash, mas eu testei em todos os três conchas.)
Janis
@ Janis: releia a documentação do bash, também mencionou sobre este. Obrigado novamente.
cuonglm
4

matriz

As alternativas mais antigas no bash (Desde o bash 3.0+) são:

$ a=(aa bb cc dd ee)
$ echo "${a[@]:(-1)}   ${a[@]: -1}   ${a[@]:(~0)}   ${a[@]:~0}"
ee   ee   ee   ee

O espaço é necessário para evitar a interpretação de :seguido por um sinal de menos -como a expansão de "${var:-abc}"(Usar valores padrão).

A ~é uma negação aritmética bit a bit (equivalente ao complemento de uma pessoa ou inverter todos os bits ). De man bash:

AVALIAÇÃO ARITMÉTICA

      ! ~         logical and bitwise negation  

Desde o bash-4.2 + também:

$ echo "${a[-1]}   ${a[(~0)]}"
ee   ee

Desde o bash 5.0 ou superior:

$ echo "${a[~0]}"
ee

Para todas as versões do bash (bash mais antigo):

$ echo "${a[   ${#a[@]}-1   ]}"    # spaces added **only** for readability
ee

@

Para argumentos posicionais (desde o bash 2.01):

$ set aa bb cc dd ee
$ echo "${@:(-1)} ${@:~0} ${@: -1} ${@:$#}   ${!#}"
ee ee ee   ee

Uma solução portátil para todos os shells é usar eval:

eval printf '"%s\n"' \"\${$#}\"
Isaac
fonte
Você tem uma referência para a sintaxe do bash 5+? Eu procurei todos os 58 casos de ~no manual e não vê-lo.
Tom Hale
... e como você faz isso $@? bash: ${@[@]:(-1)}: bad substitution
Tom Hale
11
Sim, existe uma man bashreferência (verifique a resposta expandida no título @). @TomHale
Isaac
11
O não@ é um array (bom, não totalmente um array ) no bash e não aceita o índice ( ) subscrito para argumentos individuais. Você precisa usar ou equivalente. Verifique a entrada expandida no título. @TomHale[]${@:(-1)}@
Isaac
-2

Você também pode fazer isso:

$ a=(a b c d e f)
$ echo ${a[$(expr ${#a[@]} - 1)]}

Resultado:

$ f

O que você está fazendo é obter toda a contagem de elementos na matriz e subtrair -1, devido à obtenção de todos os elementos, não começando pelo índice da matriz que é 0.

Javier Salas
fonte