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.

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
}

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
}

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
}

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


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}'
}

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}'
}

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.


+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нιη
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.