Как сделать аргумент необязательным в bash?

13

В приведенной ниже функции с 9 аргументами:

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

Я хочу, чтобы вторые аргументы для следующего (3..9) стали необязательными аргументами .

Когда я вызываю функцию с двумя аргументами, я получаю сообщение об ошибке:

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

Примечание BOLD : первый аргумент и второй аргумент являются принудительными аргументами и не являются обязательными для функции. Я только хочу, чтобы вторые аргументы к следующему были необязательными, и когда я вызываю функцию, содержащую менее 2 аргументов, функция не должна возвращать никакого результата.

αғsнιη
источник

Ответы:

22

Если вы не передадите аргументы с пробелами:

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

Эффект:

$ sum 1 2 3
6

Объяснение:

  1. <<<"some string"питается только "some string"как вход. Думайте об этом как сокращение для echo "some string" |. Это называется Здесь Строка .
  2. "$@"расширяется во все позиционные параметры, разделенные пробелами. Это эквивалентно "$1 $2 ...".
  3. Отсюда tr ' ' '+' <<<"$@"выходы "$1+$2+$3...", которые оцениваются внешними $(( )).
  4. [[ -n $2 ]]проверяет, не является ли второй параметр не пустым Вы можете заменить [[ -n $2 ]] &&на [[ -z $2 ]] ||.

Другой путь:

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

Объяснение:

  1. $*аналогично тому $@, что параметры разделены не пробелами, а первым символом разделителя внутренних полей ( IFS) . С помощью IFS=+он расширяется до "$ 1 + $ 2 + ...". См. В чем разница между $ * и $ @?
  2. Мы устанавливаем IFSвложенную оболочку (обратите внимание на окружающие скобки), чтобы основная оболочка не затрагивалась. IFSпо умолчанию: \t\n(пробел, табуляция, перевод строки). Это альтернатива использованию localпеременных.

Теперь, чтобы ответить на ваш вопрос:

Вы можете использовать значение по умолчанию для любой переменной или параметра. Или:

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

Или:

SUM() { 
 echo "The sum is $(($1+$2+${3:=0}+${4:=0}+${5:=0}+${6:=0}+${7:=0}+${8:=0}+${9:=0}))" || false
}
Мур
источник
6
Острота! Я знаю, что комментарии не предназначены для бесплатных комплиментов и благодарностей, но это решение просто ... злое! :-)
zwets
17

Посмотрите на shiftоператора. Он сместит аргументы 2 и далее в позиции 1 и далее, отбросив аргумент 1.

sum () {
    local total=0;
    while [ $# -gt 0 ]; do
        total=$(($total + $1))
        shift
    done
    echo $total
}
zwets
источник
4

Вы можете использовать рекурсивное определение, которое заканчивается, когда sumвызывается без аргументов. Мы используем тот факт, что testбез аргументов оценивает false.

sum () {
    test $1 && echo $(( $1 + $(shift; sum $@) )) || echo 0
}
zwets
источник
3

Попробуй это:

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

Это выведет 12 и 30.

$@ссылается на параметр, $#возвращает номер параметра, в данном случае 3 или 11.

Проверено на Linux Redhat 4

Лети
источник
2

Вы можете просто использовать небольшой цикл:

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

Лично я бы просто использовал perlили awkвместо этого:

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

или

sum(){
 echo "$@" | awk '{for(i=1; i<=NF; i++){k+=$i} print k}'
}
terdon
источник
2

Используйте 0 в качестве значений по умолчанию для $ 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}))"
}

От 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.

Примеры:

$ SUM

Сумма 0

$ SUM 1 2 

Сумма 3

$ SUM 1 1 1 1 1 1 1 1 1 

Сумма 9


Тот же вывод с awk:

SUM() {
  echo -e ${@/%/\\n} | awk '{s+=$1} END {print "The sum is " s}'
}
Кир
источник
1

Это также мое собственное решение, которое я попробовал и нашел:

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

$ SUM 4 6 5
The sum is 15

Но ответ @ Муру хорош.

αғsнιη
источник
+1: интересное использование двух арифметических расширений для оценки пустых параметров до нуля.
Муру
1
@Muru спасибо, но в этом случае в моем ответе мы используем не более 9 аргументов, и мы должны использовать группу аргументов для передачи более 9. Спасибо за ваш идеальный ответ.
αғsнιη