Percursos
/
Bash
Bash
/
Programa
/
Mais sobre arrays
Ma

Mais sobre arrays em Bash

{one: "1 exercício", many: "%{count} exercícios", other: "%{count} exercícios"}

Sobre Mais sobre arrays

Introduzimos os arrays no capítulo Arrays. Este documento mostra mais formas de usar arrays.

Concatenar os elementos de um array numa única string

No capítulo anterior sobre Arrays, viste "${myarray[@]}", com o índice @, a ser usado para expandir o array nos elementos individuais. Mas, às vezes, queres juntar todos os elementos numa única string. Para isso, usa o índice *:

echo "${myarray[*]}"

Tens de colocar a expansão entre aspas duplas.

O Bash usa o primeiro caráter da variável interna IFS como caráter separador. Por predefinição, $IFS é composto por espaço, tabulação e nova linha.

myarray=(one two three four)
mystring="${myarray[*]}"
declare -p mystring
# => declare -- mystring="one two three four"

Podemos manipular a variável IFS para usar um caráter separador diferente:

myarray=(one two three four)
IFS=","
mystring="${myarray[*]}"
declare -p mystring
# => declare -- mystring="one,two,three,four"
Advanced
Podemos encapsular isto numa função. Clica para ver os detalhes.
join() {
    local IFS=$1
    shift
    local elements=("$@")
    echo "${elements[*]}"
}

join ":" "${myarray[@]}"     # note, the "@" index
# => "one:two:three:four"

Localizar o IFS na função significa que não temos de guardar o valor antigo e restaurá-lo ao seu valor anterior no âmbito global.

Como refinamento, o parâmetro especial "$*", quando está entre aspas, tem a mesma funcionalidade, por isso não precisamos de guardar uma cópia dos argumentos da função:

join() {
    local IFS=$1
    shift
    echo "$*"
}
Sem usar uma função, modificar o IFS numa subshell é uma boa forma de evitar modificá-lo na shell atual. Clica para ver os detalhes.
(IFS=","; echo "${myarray[*]}")

Os parênteses criam uma subshell (uma cópia da shell atual). Quando os comandos dentro dos parênteses terminam, a subshell sai e a variável IFS alterada desaparece.

Repara que isto não funciona: IFS="," echo "${myarray[*]}" -- a expansão de parâmetros é feita primeiro, antes de a shell aplicar a variável IFS modificada ao comando echo.

Fatias de arrays

Talvez já tenhas visto a "${variable:offset:length}" expansão de parâmetros, que se expande numa substring do valor da variável. Podemos fazer a mesma coisa com arrays para expandir uma fatia do array.

myarray=(one two three four)

subarray=("${myarray[@]:0:2}")
declare -p subarray
# => declare -a subarray=([0]="one" [1]="two")

subarray=("${myarray[@]:1:3}")
declare -p subarray
# => declare -a subarray=([0]="two" [1]="three" [2]="four")

Omitir a parte do comprimento significa "desde o deslocamento até ao fim do array":

subarray=("${myarray[@]:2}")
declare -p subarray
# => declare -a subarray=([0]="three" [1]="four")

Passar um array a uma função

Isto não é tão simples como noutras linguagens que possas conhecer. Há duas técnicas principais para passar um array a uma função.

Passar os elementos

Na primeira técnica, passas todos os valores do array e recolhe-los num array local dentro da função.

myfunc() {
    local array_copy=("$@")
    # do stuff with array_copy
    declare -p array_copy
}

array_original=(11 22 33 44)
myfunc "${array_original[@]}"

O array da função contém uma cópia dos valores. Quaisquer alterações feitas ao array dentro da função não se refletem no âmbito exterior.

Passar o nome do array

Esta técnica é mais parecida com a capacidade de "passagem por referência" que possas conhecer de outras linguagens. Passas o nome do array como uma string. A função cria uma variável local com o atributo "nameref". Este array local e o array global (cujo nome passámos) são o mesmo array.

myfunc() {
    # note the `-n` option
    local -n local_array=$1

    # do stuff with local_array
    for i in "${!local_array[@]}"; do
        printf '%d => %s\n' "$i" "${local_array[i]}"
    end

    # we can mutate it
    local_array+=(55 66 77)
}

array_original=(11 22 33 44)
myfunc "array_original"

# show the mutated array
declare -p array_original
# => declare -a array_original=([0]="11" [1]="22" [2]="33" [3]="44" [4]="55" [5]="66" [6]="77")

Os namerefs também funcionam com arrays associativos e com variáveis "escalares" (que contêm um valor de string).

Note

Dentro da função, declare -p local_array não é muito útil. Limita-se a emitir declare -n local_array="array_original". Podes obter informação detalhada sobre o array inspecionando o nome do array que foi passado: declare -p "$1"

Caution

Tem cuidado para que o array local tenha um nome diferente do array que passaste. O código continua a funcionar, mas emite avisos de "circular name reference" como este:

myfunc() {
    local -n a=$1
    local IFS=,
    echo "${a[*]}"
}

# same name as the function's local variable
a=(one two three)
myfunc a
bash: local: warning: a: circular name reference
bash: warning: a: circular name reference
bash: warning: a: circular name reference
bash: warning: a: circular name reference
one,two,three

Os parâmetros posicionais são "do tipo array"

Em shells que pretendem conformar-se apenas com a norma POSIX (como o ash e o dash), não existem arrays. A forma mais próxima que consegues é usar os parâmetros posicionais.

  • Os parâmetros posicionais são acedidos por índice: $1, $2, etc.
  • São expandidos em elementos individuais com "$@"
  • São concatenados numa única string com "$*"
  • O número de parâmetros é $#

Usa o comando set para lhes atribuir valores:

set -- one two three
set -- "$@" four

for item in "$@"; do
    echo "do something with $item"
done

Se o teu objetivo é escrever scripts de shell "portáveis", vais usar os parâmetros posicionais para armazenar uma "lista" de valores.

Editar via GitHub A ligação abre numa nova janela ou separador