¿Cómo hacer que el argumento sea opcional en bash?

13

En la siguiente función con 9 argumentos:

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

Quiero hacer que los segundos argumentos para el siguiente (3..9) se conviertan en argumentos opcionales .

Cuando llamo a la función con 2 argumentos me sale un error:

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

Nota BOLD : el primer argumento y el segundo argumento son argumentos de fuerza y no son opcionales para la función. Solo quiero que los segundos argumentos para el siguiente sean opcionales y cuando llamo a la función menos de 2 args, la función no debe devolver ningún resultado.

αғsнιη
fuente

Respuestas:

22

Si no va a pasar argumentos con espacios:

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

Efecto:

$ sum 1 2 3
6

Explicación:

  1. <<<"some string"se alimenta solo "some string"como entrada. Piense en ello como una abreviatura de echo "some string" |. Se llama una cadena aquí .
  2. "$@"se expande en todos los parámetros posicionales, separados por espacios. Es equivalente a "$1 $2 ...".
  3. Por lo tanto, tr ' ' '+' <<<"$@"salidas "$1+$2+$3...", que es evaluada por el exterior $(( )).
  4. [[ -n $2 ]]prueba si el segundo parámetro no está vacío. Podrías reemplazar [[ -n $2 ]] &&con [[ -z $2 ]] ||.

De otra manera:

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

Explicación:

  1. $*es igual que $@, excepto que los parámetros no están separados por espacios, sino por el primer carácter del separador de campo interno ( IFS) . Con IFS=+, se expande a "$ 1 + $ 2 + ...". Consulte ¿Cuál es la diferencia entre $ * y $ @?
  2. Lo configuramos IFSen un subshell (tenga en cuenta los paréntesis que lo rodean) para que el shell principal no se vea afectado. IFSes, por defecto: \t\n(espacio, tabulación, nueva línea). Esta es una alternativa al uso de localvariables.

Ahora para responder a tu pregunta:

Puede usar un valor predeterminado para cualquier variable o parámetro. Ya sea:

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

O:

SUM() { 
 echo "The sum is $(($1+$2+${3:=0}+${4:=0}+${5:=0}+${6:=0}+${7:=0}+${8:=0}+${9:=0}))" || false
}
muru
fuente
66
¡Hábil! Sé que los comentarios no están destinados a cumplidos gratuitos y gracias, pero esta solución es simplemente ... ¡perversa! :-)
zwets
17

Echa un vistazo al shiftoperador. Desplazará los argumentos 2 y hacia adelante a las posiciones 1 y hacia adelante, descartando el argumento 1.

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

Podría usar una definición recursiva que termina cuando sumse invoca sin argumentos. Hacemos uso del hecho de que testsin argumentos se evalúa false.

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

Prueba esto:

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

Esto generará 12 y 30.

$@se refiere al parámetro, $#devuelve el número de parámetro, en este caso 3 u 11.

Probado en Linux Redhat 4

Lety
fuente
2

Podrías usar un pequeño bucle:

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

Personalmente, solo usaría perlo en su awklugar:

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

o

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

Use 0 como valores predeterminados de $ 1 a $ 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.

Ejemplos:

$ SUM

La suma es 0

$ SUM 1 2 

La suma es 3

$ SUM 1 1 1 1 1 1 1 1 1 

La suma es 9


Misma salida con awk:

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

También es mi propia solución. Lo probé y encontré:

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

$ SUM 4 6 5
The sum is 15

Pero la respuesta de @ muru es buena.

αғsнιη
fuente
+1: Uso interesante de dos expansiones aritméticas para evaluar parámetros vacíos a cero.
muru
1
@muru gracias, pero en este caso mi respuesta no usamos más de 9 argumentos y tenemos que usar un grupo de argumentos para pasar más de 9. Gracias por su respuesta que es perfecta.
αғsнιη