Introduzimos os arrays no capítulo Arrays. Este documento mostra mais formas de usar arrays.
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"
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 "$*"
}
(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.
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")
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.
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.
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).
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"
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
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.
$1, $2, etc."$@"
"$*"
$#
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.