Estou escrevendo um script bash que tem set -u
, e tenho um problema com a expansão da matriz vazia: o bash parece tratar uma matriz vazia como uma variável não definida durante a expansão:
$ set -u
$ arr=()
$ echo "foo: '${arr[@]}'"
bash: arr[@]: unbound variable
( declare -a arr
também não ajuda.)
Uma solução comum para isso é usar em ${arr[@]-}
vez disso, substituindo assim uma string vazia em vez do array vazio ("indefinido"). No entanto, esta não é uma boa solução, pois agora você não pode discernir entre um array com uma única string vazia e um array vazio. (@ -expansion é especial no bash, ele se expande "${arr[@]}"
para "${arr[0]}" "${arr[1]}" …
, o que o torna uma ferramenta perfeita para construir linhas de comando.)
$ countArgs() { echo $#; }
$ countArgs a b c
3
$ countArgs
0
$ countArgs ""
1
$ brr=("")
$ countArgs "${brr[@]}"
1
$ countArgs "${arr[@]-}"
1
$ countArgs "${arr[@]}"
bash: arr[@]: unbound variable
$ set +u
$ countArgs "${arr[@]}"
0
Portanto, há uma maneira de contornar esse problema, além de verificar o comprimento de uma matriz em um if
(veja o exemplo de código abaixo), ou desativar a -u
configuração para esse pequeno pedaço?
if [ "${#arr[@]}" = 0 ]; then
veryLongCommandLine
else
veryLongCommandLine "${arr[@]}"
fi
Atualização:bugs
Tag removida devido à explicação de ikegami.
"${arr[@]}"
. Estou esquecendo de algo? Pelo que posso ver, funciona pelo menos em5.x
.De acordo com a documentação,
Nenhum subscrito foi atribuído a um valor, então a matriz não está definida.
Mas, embora a documentação sugira que um erro é apropriado aqui, esse não é mais o caso desde o 4.4 .
Há uma condição que você pode usar embutida para conseguir o que deseja nas versões anteriores: Use em
${arr[@]+"${arr[@]}"}
vez de"${arr[@]}"
.Testado com bash 4.2.25 e 4.3.11.
fonte
[@]+
realmente faz e por que o segundo${arr[@]}
não causa um erro ilimitado.${parameter+word}
sóword
se expande separameter
não estiver desarmado.${arr+"${arr[@]}"}
é mais curto e parece funcionar tão bem.unset arr
,arr[1]=a
,args ${arr+"${arr[@]}"}
Vsargs ${arr[@]+"${arr[@]}"}
+
expansão não ocorre (ou seja, uma matriz vazia), a expansão é substituída por nada , que é exatamente o que uma matriz vazia se expande.:+
não é seguro porque também trata uma('')
matriz de elemento único como indefinida e de forma semelhante se expande para nada, perdendo o valor.A resposta aceita de @ikegami está sutilmente errada! O encantamento correto é
${arr[@]+"${arr[@]}"}
:fonte
bash-4.4.23
:arr=('') && countArgs "${arr[@]:+${arr[@]}}"
produz1
. Mas a${arr[@]+"${arr[@]}"}
forma permite diferenciar entre valor vazio / não vazio adicionando / não adicionando dois pontos.arr=('') && countArgs ${arr[@]:+"${arr[@]}"}
->0
,arr=('') && countArgs ${arr[@]+"${arr[@]}"}
->1
.Acontece que o manuseio do array foi alterado no recentemente lançado (2016/09/16) bash 4.4 (disponível no Debian stretch, por exemplo).
Agora, a expansão de matrizes vazias não emite aviso
fonte
bash-4.4.12
"${arr[@]}"
seria suficiente.esta pode ser outra opção para aqueles que preferem não duplicar arr [@] e estão bem em ter uma string vazia
testar:
fonte
for
isso acabará com uma única string vazia quando a matriz for indefinida / definida como vazia, onde como você pode querer o corpo do loop para não ser executado se a matriz não estiver definida.${arr[@]+"${arr[@]}"}
, preserva corretamente o estado de matriz vazia.A resposta de @ikegami está correta, mas considero a sintaxe
${arr[@]+"${arr[@]}"}
terrível. Se você usar nomes de variáveis de matriz longa, começa a parecer espaguete mais rápido do que o normal.Em vez disso, tente isto:
Parece que o operador de fatia da matriz Bash é muito complacente.
Então, por que Bash dificultou tanto o tratamento do caso extremo dos arrays? Suspiro. Não posso garantir que sua versão permitirá tal abuso do operador de fatia de array, mas funciona muito bem para mim.
Advertência: Estou usando
GNU bash, version 3.2.25(1)-release (x86_64-redhat-linux-gnu)
sua milhagem pode variar.fonte
"${arr[@]:0}"
dá-bash: arr[@]: unbound variable
.arr=("_dummy_")
e usar a expansão em${arr[@]:1}
todos os lugares. Isso é citado em outras respostas, referindo-se aos valores sentinela.De fato, inconsistência "interessante".
Além disso,
Embora eu concorde que o comportamento atual pode não ser um bug no sentido que @ikegami explica, IMO podemos dizer que o bug está na definição (de "set") em si, e / ou no fato de que é aplicado de forma inconsistente. O parágrafo anterior na página de manual diz
o que é inteiramente consistente com o que diz sobre a expansão dos parâmetros posicionais em
"$@"
. Não que não haja outras inconsistências nos comportamentos de matrizes e parâmetros posicionais ... mas para mim não há indícios de que esse detalhe seja inconsistente entre os dois.Continuando,
Portanto,
arr[]
não é tão desvinculado que não podemos obter uma contagem de seus elementos (0) ou uma lista (vazia) de suas chaves? Para mim, eles são sensatos e úteis - o único valor atípico parece ser o${arr[@]}
(e${arr[*]}
) expansão.fonte
Estou complementando no @ ikegami (aceito) e no @kevinarpe (também bom).
Você pode fazer
"${arr[@]:+${arr[@]}}"
para contornar o problema. O lado direito (ou seja, depois:+
) fornece uma expressão que será usada caso o lado esquerdo não seja definido / nulo.A sintaxe é misteriosa. Observe que o lado direito da expressão sofrerá expansão de parâmetros, portanto, atenção extra deve ser dada para ter citações consistentes.
Como @kevinarpe menciona, uma sintaxe menos misteriosa é usar a notação de fatia de array
${arr[@]:0}
(nas versões Bash>= 4.4
), que se expande para todos os parâmetros, começando do índice 0. Também não requer tanta repetição. Essa expansão funciona independentementeset -u
, então você pode usar isso a qualquer momento. A página do manual diz (em Expansão de parâmetro ):Este é o exemplo fornecido por @kevinarpe, com formatação alternativa para colocar a saída em evidência:
Esse comportamento varia com as versões do Bash. Você também deve ter notado que o operador de comprimento
${#arr[@]}
sempre avaliará como0
para matrizes vazias, independentemente deset -u
, sem causar um 'erro de variável não acoplada'.fonte
:0
idioma falha no Bash 4.2, então essa não é uma abordagem segura. Veja minha resposta .Aqui estão algumas maneiras de fazer algo assim, uma usando sentinelas e outra usando anexos condicionais:
fonte
Inconsistência interessante; isso permite que você defina algo que "não é considerado definido", mas aparece na saída de
declare -p
ATUALIZAÇÃO: conforme outros mencionados, corrigido no 4.4 divulgado após a publicação desta resposta.
fonte
echo ${arr[@]}
(mas antes do Bash 4.4 você ainda verá um erro).echo $arr[@]
você mesmo, teria visto que a mensagem de erro é diferente.A forma mais simples e compatível parece ser:
fonte