如何在bash中将参数设为可选?


13

在下面带有9个参数的函数中:

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

我想使next(3..9)第二个参数成为可选参数

当我使用2个参数调用函数时,出现错误:

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

注意BOLD:第一个参数和第二个参数是强制参数,对于函数不是可选的。我只希望下一个的第二个参数是可选的,并且当我调用小于2个args的函数时,该函数必须不返回任何结果。

Answers:


22

如果您不使用空格传递参数:

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

影响:

$ sum 1 2 3
6

说明:

  1. <<<"some string""some string"作为输入。认为它是的简写echo "some string" |。它称为Here 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
}

4

您可以使用递归定义,该定义在sum不带参数的情况下调用时终止。我们利用这样的事实,即test无参数的结果为false

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

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

但是就我个人而言,我只是使用perlawk代替:

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

要么

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

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

但是@muru的答案很好。


+1:有趣地使用两个算术展开来将空参数评估为零。
muru 2014年

1
@muru谢谢,但是在这种情况下,我的回答是我们不会使用超过9个参数,而我们必须使用参数组来传递9个以上的参数。谢谢您的完美回答。
αғsнιη
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.