Comment rendre l'argument optionnel dans bash?

13

Dans la fonction ci-dessous avec 9 arguments:

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

Je veux faire du deuxième argument le suivant (3..9) devenir un argument facultatif .

Lorsque j'appelle la fonction avec 2 arguments, j'obtiens une erreur:

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

Remarque BOLD : le premier argument et le deuxième argument sont des arguments de force et ne sont pas facultatifs pour la fonction. Je veux seulement que les deuxièmes arguments au suivant soient optionnels et quand j'appelle la fonction moins de 2 arguments, la fonction ne doit retourner aucun résultat.

αғsнιη
la source

Réponses:

22

Si vous ne passez pas d'arguments avec des espaces:

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

Effet:

$ sum 1 2 3
6

Explication:

  1. <<<"some string"alimente uniquement "some string"en entrée. Considérez cela comme un raccourci pour echo "some string" |. Il s'agit d'une chaîne Here .
  2. "$@"se développe dans tous les paramètres positionnels, séparés par des espaces. C'est équivalent à "$1 $2 ...".
  3. Par conséquent, les tr ' ' '+' <<<"$@"sorties "$1+$2+$3...", qui sont évaluées par l'extérieur $(( )).
  4. [[ -n $2 ]]teste si le deuxième paramètre n'est pas vide. Vous pouvez remplacer [[ -n $2 ]] &&par [[ -z $2 ]] ||.

Autrement:

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

Explication:

  1. $*est identique $@, sauf que les paramètres ne sont pas séparés par des espaces, mais par le premier caractère du séparateur de champ interne ( IFS) . Avec IFS=+, il se développe à "$ 1 + $ 2 + ...". Voir Quelle est la différence entre $ * et $ @?
  2. Nous définissons IFSun sous-shell (notez les parenthèses environnantes) afin que le shell principal ne soit pas affecté. IFSest, par défaut: \t\n(espace, tabulation, nouvelle ligne). Il s'agit d'une alternative à l'utilisation de localvariables.

Maintenant, pour répondre à votre question:

Vous pouvez utiliser une valeur par défaut pour n'importe quelle variable ou paramètre. Soit:

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
la source
6
Nifty! Je sais que les commentaires ne sont pas destinés aux compliments gratuits et merci, mais cette solution est juste ... méchante! :-)
zwets
17

Jetez un œil à l' shiftopérateur. Il déplacera les arguments 2 et suivants vers les positions 1 et suivantes, rejetant l'argument 1.

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

Vous pouvez utiliser une définition récursive qui se termine quand sumest invoquée sans arguments. Nous utilisons le fait que testsans arguments est évalué à false.

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

Essaye ça:

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

Cela produira 12 et 30.

$@fait référence au paramètre, $#renvoie le nombre de paramètres, dans ce cas 3 ou 11.

Testé sur Linux Redhat 4

Lety
la source
2

Vous pouvez simplement utiliser une petite boucle:

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

Personnellement, j'utiliserais simplement perlou à la awkplace:

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

ou

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

Utilisez 0 comme valeurs par défaut pour 1 $ à 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.

Exemples:

$ SUM

La somme est 0

$ SUM 1 2 

La somme est de 3

$ SUM 1 1 1 1 1 1 1 1 1 

La somme est 9


Même sortie avec awk:

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

C'est aussi ma propre solution, je l'ai essayée et j'ai trouvé:

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

$ SUM 4 6 5
The sum is 15

Mais la réponse de @ muru est bonne.

αғsнιη
la source
+1: Utilisation intéressante de deux extensions arithmétiques pour évaluer les paramètres vides à zéro.
muru
1
@muru merci, mais dans ce cas ma réponse nous n'utilisons pas plus de 9 arguments et nous devons utiliser un groupe d'arguments pour passer plus de 9. merci pour votre réponse qui est parfaite.
αғsнιη