A expansão de parâmetros na expressão de teste é possível?

A expansão de parâmetros na expressão de teste é possível?

Quando tento a seguinte expressão, bashrecebo uma mensagem de erro estranha:

[: -lt: unary operator expected

Primeiro a definição da função

some_func () {
  (( 3 + 5 ))
}

E a expressão

[ $(some_func) -lt 10 ] && true

Acho que o problema é misturar operadores como -ltsubstituição de comando e/ou expansão aritmética?

O código de saída $?é 2 e a mensagem éan unary op was expected.

Responder1

Deveria ser

some_func() {
  echo "$(( 3 + 5 ))"
}
[ "$(some_func)" -lt 10 ]

Por que falhou

$(some_func)se expande para osaídada função* (menos a nova linha final), que, no entanto, não produz nada. Portanto, o teste torna-se

[ -lt 10 ]

Nas suas formas mais básicas,o [teste aceita 1 a 3 parâmetros. Como acima existem 2 parâmetros, o Bash espera que o primeiro seja um operador unário. -lté binário, daí a mensagem de erro.

Você tinhacitou a expansão conforme apropriadocom

[ "$(some_func)" -lt 10 ]

O erro seria "expressão inteira esperada" porque o teste teria a string vazia:

[ "" -lt 10 ]

E, a menos que sobcircunstâncias não triviais, && trueé redundante.

*Como a expansão não está cotada, a produção também sofredivisão de palavraseexpansão do nome do arquivo. Eles não devem desempenhar um papel no presente exemplo, desde que $IFSseu valor padrão não tenha sido alterado.

Responder2

$(cmd)recebe osaída padrãode cmd¹, então para que isso se expanda para o resultado, você cmdprecisasaídaisto:

some_func() {
  echo "$(( 3 + 5 ))"
}
[ "$(some_func)" -lt 10 ]

como outros já disseram. No entanto, isso significa que some_funcé executado em um ambiente subshell, portanto, qualquer modificação nas variáveis ​​ou qualquer outra coisa será perdida posteriormente.

Por exemplo, não adianta fazer:

counter=0
incr() { echo "$((++counter))"; }
while [ "$(incr)" -le 10 ]...

as $(incr)sempre será expandido para 1 (as countersó será incrementado no subshell).

Para que uma função retorne um resultado numérico por meio de umavaliação aritmética, você precisaria de um shell com suporte parafunções matemáticascomo ksh93ou zsh. bashnão servirá.

Em zsh:

counter=0
incr() (( ++counter ))
functions -M incr

while (( incr() <= 10 )); do
  print $counter
done

Em ksh93:

function .sh.math.incr i {
  # ksh93 functions must take at least one argument
  (( .sh.value = (counter += i) ))
}
while (( incr(1) <= 10 )); do
  print "$counter"
done

Outra alternativa no ksh93 ou em versões recentes do mksh é usar formas de substituição de comando que não introduzam subshells:

counter=0
function incr { print "$(( ++counter ))"; }
while [ "${ incr; }" -le 10 ]; do
  print "$counter"
done

Ou em mksh:

counter=0
incr() (( REPLY = ++counter ))
while [ "${| incr; }" -le 10 ]; do
  print "$counter"
done

Em qualquer shell POSIX incluindo bash, você sempre pode retornar o valor em uma variável predefinida ( $REPLYé comumente usada para isso):

counter=0
incr() { REPLY=$(( counter += 1 )); }
while incr; [ "$REPLY" -le 10 ]; do
  echo "$counter"
done

Você encontrará mais detalhes emesta resposta para outra pergunta e resposta sobre o recurso valsub do mksh


¹ sem seus caracteres de nova linha finais e, no caso de bashseus caracteres NUL, e aqui porque você esqueceu as aspas, sujeito a split + glob

Responder3

Substituição de comandocaptura a saídade um comando ou função. Essa função não tem saída.

A $?variável contém oCódigo de retornoda função.

Faça isso:

some_func
(( $? < 10 )) && echo yes

ou altere a função para:

some_func() {
    echo $(( 3 + 5 ))
}

[[ $(some_func) -lt 10 ]] && echo yes

Observe como estou usando [[...]]em vez de [...]? A condicional de colchete duplo é mais indulgente com valores vazios.

informação relacionada