Jak ustawić argument jako opcjonalny w bash?

13

Poniżej funkcja z 9 argumentami:

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

Chcę, aby drugi argument do następnego (3..9) stał się argumentem opcjonalnym .

Gdy wywołuję funkcję z 2 argumentami, pojawia się błąd:

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

Uwaga BOLD : pierwszy argument i drugi argument są argumentami wymuszającymi i nie są opcjonalne dla funkcji. Chcę tylko, aby drugi argument do następnego był opcjonalny, a gdy wywołam funkcję mniej niż 2 argumenty, funkcja nie może zwrócić żadnego wyniku.

αғsнιη
źródło

Odpowiedzi:

22

Jeśli nie podasz argumentów ze spacjami:

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

Efekt:

$ sum 1 2 3
6

Wyjaśnienie:

  1. <<<"some string"wprowadza tylko "some string"jako dane wejściowe. Pomyśl o tym jak o skrócie echo "some string" |. Nazywa się to tutaj ciągiem .
  2. "$@"rozwija się do wszystkich parametrów pozycyjnych, oddzielonych spacjami. Jest to równoważne z "$1 $2 ...".
  3. Stąd tr ' ' '+' <<<"$@"wyniki "$1+$2+$3...", które są oceniane przez zewnętrzne $(( )).
  4. [[ -n $2 ]]sprawdza, czy drugi parametr nie jest pusty. Można wymienić [[ -n $2 ]] &&z [[ -z $2 ]] ||.

Inny sposób:

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

Wyjaśnienie:

  1. $*jest tak samo $@, z tym wyjątkiem, że parametry nie są oddzielone spacjami, ale pierwszym znakiem Internal Field Separator ( IFS) . Z IFS=+rozwija się do „1 $ + 2 $ + ...”. Zobacz Jaka jest różnica między $ * a $ @?
  2. Ustawiamy IFSw podpowłoce (zwróć uwagę na otaczające nawiasy), aby główna powłoka nie uległa zmianie. IFSjest domyślnie: \t\n(spacja, tabulator, nowa linia). Jest to alternatywa dla używania localzmiennych.

Teraz, aby odpowiedzieć na twoje pytanie:

Możesz użyć wartości domyślnej dla dowolnej zmiennej lub parametru. Zarówno:

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

Lub:

SUM() { 
 echo "The sum is $(($1+$2+${3:=0}+${4:=0}+${5:=0}+${6:=0}+${7:=0}+${8:=0}+${9:=0}))" || false
}
muru
źródło
6
Sprytne! Wiem, że komentarze nie są przeznaczone dla nieuzasadnionych komplementów i dzięki, ale to rozwiązanie jest po prostu ... niegodziwe! :-)
zwets
17

Spójrz na shiftoperatora. Przenosi argumenty 2 i dalej do pozycji 1 i dalej, odrzucając argument 1.

sum () {
    local total=0;
    while [ $# -gt 0 ]; do
        total=$(($total + $1))
        shift
    done
    echo $total
}
zwets
źródło
4

Możesz użyć definicji rekurencyjnej, która kończy się, gdy sumzostanie wywołana bez argumentów. Korzystamy z faktu, że testbez argumentów ocenia false.

sum () {
    test $1 && echo $(( $1 + $(shift; sum $@) )) || echo 0
}
zwets
źródło
3

Spróbuj tego:

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

To da 12 i 30.

$@odnosi się do parametru, $#zwraca liczbę parametru, w tym przypadku 3 lub 11.

Testowane na linhat redhat 4

Lety
źródło
2

Możesz po prostu użyć małej pętli:

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

Osobiście jednak użyłbym perllub awkzamiast tego:

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

lub

sum(){
 echo "$@" | awk '{for(i=1; i<=NF; i++){k+=$i} print k}'
}
terdon
źródło
2

Użyj 0 jako wartości domyślnych od 1 do 9 USD:

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

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

Przykłady:

$ SUM

Suma wynosi 0

$ SUM 1 2 

Suma wynosi 3

$ SUM 1 1 1 1 1 1 1 1 1 

Suma wynosi 9


Ta sama wydajność z awk:

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

To także moje własne rozwiązanie. Wypróbowałem to i znalazłem:

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

$ SUM 4 6 5
The sum is 15

Ale odpowiedź @ muru jest dobra.

αғsнιη
źródło
+1: Interesujące użycie dwóch rozszerzeń arytmetycznych do oceny pustych parametrów do zera.
muru
1
@muru dzięki, ale w tym przypadku moja odpowiedź nie używa więcej niż 9 argumentów i musimy użyć grupy argumentów, aby przekazać więcej niż 9. dziękuję za odpowiedź, która jest idealna.
αғsнιη