Como tornar o argumento opcional no bash?

13

Na função abaixo com 9 argumentos:

SUM() { 
    echo "The sum is $(($1+$2+$3+$4+$5+$6+$7+$8+$9))"
}

Eu quero fazer com que os segundos argumentos para o próximo (3..9) se tornem argumentos opcionais .

Quando eu chamo a função com 2 argumentos, recebo um erro:

SUM 3 8
bash: 3+8+++++++: syntax error: operand expected (error token is "+")

Nota Negrito : o primeiro argumento e o segundo argumento são argumentos de força e não são opcionais para a função. Eu só quero que os segundos argumentos para o próximo sejam opcionais e quando chamo a função com menos de 2 args, a função não deve retornar nenhum resultado.

αғsнιη
fonte

Respostas:

22

Se você não passar argumentos com espaços:

sum() {  
[[ -n $2 ]] && echo $(( $(tr ' ' '+' <<<"$@") ))
}

Efeito:

$ sum 1 2 3
6

Explicação:

  1. <<<"some string"alimenta apenas "some string"como entrada. Pense nisso como um atalho paraecho "some string" | . É chamado de String Aqui .
  2. "$@"expande em todos os parâmetros posicionais, separados por espaços. É equivalente a "$1 $2 ...".
  3. Portanto, tr ' ' '+' <<<"$@"saídas"$1+$2+$3..." , que são avaliadas pelo externo $(( )).
  4. [[ -n $2 ]]testa se o segundo parâmetro não está vazio. Você pode substituir [[ -n $2 ]] &&por[[ -z $2 ]] || .

Outra maneira:

sum() {
[[ -n $2 ]] && (IFS=+; echo $(( $* )))
}

Explicação:

  1. $*é como $@, exceto que os parâmetros não são separados por espaços, mas pelo primeiro caractere do Internal Field Separator ( IFS) . ComIFS=+ , ele se expande para "$ 1 + $ 2 + ...". Consulte Qual é a diferença entre $ * e $ @?
  2. Definimos IFSum subshell (observe os parênteses ao redor) para que o shell principal não seja afetado. IFSé, por padrão: \t\n(espaço, guia, nova linha). Esta é uma alternativa ao usolocal variáveis.

Agora respondendo à sua questão:

Você pode usar um valor padrão para qualquer variável ou parâmetro. Ou:

SUM() { 
 echo "The sum is $(($1+$2+${3:-0}+${4:-0}+${5:-0}+${6:-0}+${7:-0}+${8:-0}+${9:-0}))" || false
}

Ou:

SUM() { 
 echo "The sum is $(($1+$2+${3:=0}+${4:=0}+${5:=0}+${6:=0}+${7:=0}+${8:=0}+${9:=0}))" || false
}
muru
fonte
6
Bacana! Sei que os comentários não são feitos para elogios e agradecimentos gratuitos, mas essa solução é ... malvada! :-)
zwets 10/09
17

Dê uma olhada no shiftoperador. Ele mudará os argumentos 2 e seguintes para as posições 1 e seguintes, descartando o argumento 1.

sum () {
    local total=0;
    while [ $# -gt 0 ]; do
        total=$(($total + $1))
        shift
    done
    echo $total
}
zwets
fonte
4

Você pode usar uma definição recursiva que termina quando sumé chamada sem argumentos. Utilizamos o fato de que, testsem argumentos, é avaliado false.

sum () {
    test $1 && echo $(( $1 + $(shift; sum $@) )) || echo 0
}
zwets
fonte
3

Tente o seguinte:

SUM () {
 [ $# -lt "2" ] && return 1
 for par in $@; do
   local sum=`expr $sum + $par`
 done
 echo $sum
 return 0
}

SUM 3 4 5
SUM 3 4 5 1 1 1 1 2 3 4 5

Isso produzirá 12 e 30.

$@refere-se ao parâmetro, $#retorna o número do parâmetro, neste caso 3 ou 11.

Testado em linux redhat 4

Lety
fonte
2

Você pode usar um pequeno loop:

sum(){
    t=0;
    for i in "$@"; do t=$((t + i )); done
    echo $t;
}

Pessoalmente, porém, eu tinha acabado de usar perlou awkem vez disso:

sum(){
 echo "$@" | perl -lane '$s+=$_ for @F; print $s'
}

ou

sum(){
 echo "$@" | awk '{for(i=1; i<=NF; i++){k+=$i} print k}'
}
Terdon
fonte
2

Use 0 como valores padrão de US $ 1 a US $ 9:

SUM() { 
    echo "The sum is $((${1:-0}+${2:-0}+${3:-0}+${4:-0}+${5:-0}+${6:-0}+${7:-0}+${8:-0}+${9:-0}))"
}

De man bash:

${parameter:-word}
    Use Default Values. If parameter is unset or null, the expansion
    of word is substituted. Otherwise, the value of parameter is
    substituted.

Exemplos:

$ SUM

A soma é 0

$ SUM 1 2 

A soma é 3

$ SUM 1 1 1 1 1 1 1 1 1 

A soma é 9


Mesma saída com awk:

SUM() {
  echo -e ${@/%/\\n} | awk '{s+=$1} END {print "The sum is " s}'
}
Cyrus
fonte
1

Também é minha própria solução, tentei e encontrei:

SUM() { 
    echo "The sum is $(($1+$2+$[$3]+$[$4]+$[$5]+$[$6]+$[$7]+$[$8]+$[$9]))"
 }

$ SUM 4 6 5
The sum is 15

Mas a resposta de @ muru é boa.

αғsнιη
fonte
+1: uso interessante de duas expansões aritméticas para avaliar zero os parâmetros vazios.
muru
1
@ muru obrigado, mas neste caso minha resposta não usamos mais de 9 argumentos e temos que usar um grupo de argumentos para passar mais de 9. obrigado por sua resposta que é perfeita.
αғsнιη