Trilhas
/
Bash
Bash
/
Programa
/
Mais sobre arrays
Ma

Mais sobre arrays em Bash

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

Sobre Mais sobre arrays

Apresentamos os arrays no capítulo Arrays. Este documento mostra outras formas de usar arrays.

Concatenando os elementos de um array em uma única string

No capítulo anterior sobre arrays, você viu "${myarray[@]}", com o índice @, usado para expandir o array nos elementos individuais. Mas às vezes você quer juntar todos os elementos em uma única string. Para isso, use o índice *:

echo "${myarray[*]}"

É obrigatório envolver a expansão em aspas duplas.

O Bash usa o primeiro caractere da variável embutida IFS como caractere separador. Por padrão, $IFS é composto de espaço, tabulação e quebra de 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 caractere separador diferente:

myarray=(one two three four)
IFS=","
mystring="${myarray[*]}"
declare -p mystring
# => declare -- mystring="one,two,three,four"
Advanced
Podemos encapsular isso em uma função. Clique 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 dentro da função significa que não precisamos salvar o valor antigo e restaurá-lo ao valor anterior no escopo global.

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

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

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

Observe que isso não vai funcionar: IFS="," echo "${myarray[*]}" -- a expansão de parâmetro é realizada primeiro, antes de o shell aplicar a variável IFS modificada ao comando echo.

Fatias de array

Talvez você já tenha visto a expansão de parâmetro "${variable:offset:length}", que expande para uma 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 "do offset até o fim do array":

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

Passando um array para uma função

Isso não é tão simples quanto em outras linguagens que você talvez conheça. Existem duas técnicas principais para passar um array para uma função.

Passar os elementos

Na primeira técnica, você passa todos os valores do array e os recolhe em um 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 no array dentro da função não se refletem no escopo externo.

Passar o nome do array

Essa técnica se parece mais com o recurso de "passagem por referência" que você talvez conheça de outras linguagens. Você passa o nome do array como uma string. A função cria uma variável local com o atributo "nameref". Esse array local e o array global (cujo nome passamos) 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")

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. Ele apenas emite declare -n local_array="array_original". Você pode obter informações detalhadas sobre o array inspecionando o nome do array passado: declare -p "$1"

Caution

Tome cuidado para que o array local tenha um nome diferente do array passado. O código ainda vai 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 "semelhantes a arrays"

Em shells que buscam seguir apenas o padrão POSIX (como ash e dash), não existem arrays. O mais próximo que você consegue chegar é usar os parâmetros posicionais.

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

Use o comando set para atribuir valores a eles:

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

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

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

Editar via GitHub O link abre em uma nova janela ou aba