Wie mache ich das Argument in bash als optional?

13

In der folgenden Funktion mit 9 Argumenten:

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

Ich möchte die zweiten Argumente zum nächsten (3..9) zu optionalen Argumenten machen .

Wenn ich die Funktion mit 2 Argumenten aufrufe, erhalte ich eine Fehlermeldung:

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

Anmerkung FETT : Erstes Argument und zweites Argument sind Force-Argumente und für Funktionen nicht optional. Ich möchte nur, dass zweite Argumente zum nächsten optional sind, und wenn ich die Funktion mit weniger als 2 Argumenten aufrufe, muss die Funktion kein Ergebnis zurückgeben.

αғsнιη
quelle

Antworten:

22

Wenn Sie keine Argumente mit Leerzeichen übergeben:

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

Bewirken:

$ sum 1 2 3
6

Erläuterung:

  1. <<<"some string"speist nur "some string"als Eingang ein. Betrachten Sie es als Abkürzung für echo "some string" |. Es heißt Here String .
  2. "$@"Erweitert alle Positionsparameter, die durch Leerzeichen getrennt sind. Es ist äquivalent zu "$1 $2 ...".
  3. Also tr ' ' '+' <<<"$@"Ausgänge "$1+$2+$3...", die vom Äußeren ausgewertet werden $(( )).
  4. [[ -n $2 ]]prüft, ob der zweite Parameter nicht leer ist. Sie könnten ersetzen [[ -n $2 ]] &&mit [[ -z $2 ]] ||.

Ein anderer Weg:

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

Erläuterung:

  1. $*ist genauso $@, nur dass die Parameter nicht durch Leerzeichen, sondern durch das erste Zeichen des Internal Field Separator ( IFS) getrennt sind . Mit IFS=+wird es auf "$ 1 + $ 2 + ..." erweitert. Siehe Was ist der Unterschied zwischen $ * und $ @?
  2. Wir setzen IFSeine Unterschale (beachten Sie die umgebenden Klammern), damit die Hauptschale nicht betroffen ist. IFSist standardmäßig \t\n(Leerzeichen, Tabulator, Zeilenvorschub). Dies ist eine Alternative zur Verwendung von localVariablen.

Nun zur Beantwortung Ihrer Frage:

Sie können für jede Variable oder jeden Parameter einen Standardwert verwenden . Entweder:

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

Oder:

SUM() { 
 echo "The sum is $(($1+$2+${3:=0}+${4:=0}+${5:=0}+${6:=0}+${7:=0}+${8:=0}+${9:=0}))" || false
}
muru
quelle
6
Raffiniert! Ich weiß, Kommentare sind nicht für unentgeltliche Komplimente und Dankbarkeit gedacht, aber diese Lösung ist einfach ... böse! :-)
zwets
17

Schauen Sie sich den shiftBediener an. Es verschiebt Argumente 2 und weiter auf Position 1 und weiter und verwirft Argument 1.

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

Sie können eine rekursive Definition verwenden, die endet, wenn sie sumohne Argumente aufgerufen wird. Wir machen uns die Tatsache testzunutze , dass ohne Argumente ausgewertet wird false.

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

Versuche dies:

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

Dies gibt 12 und 30 aus.

$@bezieht sich auf Parameter, $#gibt die Nummer des Parameters zurück, in diesem Fall 3 oder 11.

Getestet unter Linux Red Hat 4

Lety
quelle
2

Du könntest einfach eine kleine Schleife benutzen:

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

Persönlich würde ich nur perloder awkverwenden:

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

oder

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

Verwenden Sie 0 als Standardwerte für $ 1 bis $ 9:

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

Von 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.

Beispiele:

$ SUM

Die Summe ist 0

$ SUM 1 2 

Die Summe ist 3

$ SUM 1 1 1 1 1 1 1 1 1 

Die Summe ist 9


Gleiche Ausgabe mit awk:

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

Es ist auch meine eigene Lösung, die ich ausprobiert und gefunden habe:

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

$ SUM 4 6 5
The sum is 15

Aber @ Murus Antwort ist gut.

αғsнιη
quelle
+1: Interessante Verwendung von zwei arithmetischen Erweiterungen , um leere Parameter auf Null auszuwerten.
muru
1
@muru Dank, aber in diesem Fall meine Antwort verwenden wir mehr nicht als 9 Argumente , und wir haben Gruppe von Argumenten verwenden mehr passieren als 9. Sie , dass die perfekt für Ihre Antwort danken.
αғsнιη