Làm thế nào để làm cho đối số là tùy chọn trong bash?


13

Trong hàm dưới với 9 đối số:

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

Tôi muốn làm cho các đối số thứ hai sang tiếp theo (3..9) trở thành một đối số tùy chọn .

Khi tôi gọi hàm với 2 đối số, tôi gặp lỗi:

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

Lưu ý ĐẬM : đối số thứ nhất và đối số thứ hai là đối số lực và không phải là tùy chọn cho hàm. Tôi chỉ muốn các đối số thứ hai tiếp theo là tùy chọn và khi tôi gọi hàm nhỏ hơn 2 đối số thì hàm phải trả về không có kết quả.

Câu trả lời:


22

Nếu bạn sẽ không vượt qua đối số với khoảng trắng:

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

Hiệu ứng:

$ sum 1 2 3
6

Giải trình:

  1. <<<"some string"nguồn cấp dữ liệu chỉ "some string"là đầu vào. Hãy nghĩ về nó như tốc ký cho echo "some string" |. Nó được gọi là Chuỗi ở đây .
  2. "$@"mở rộng thành tất cả các tham số vị trí, cách nhau bởi khoảng trắng. Nó tương đương với"$1 $2 ..." .
  3. Do đó, tr ' ' '+' <<<"$@"đầu ra "$1+$2+$3...", được đánh giá bởi bên ngoài$(( )) .
  4. [[ -n $2 ]]kiểm tra nếu tham số thứ hai không trống. Bạn có thể thay thế [[ -n $2 ]] &&bằng [[ -z $2 ]] ||.

Cách khác:

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

Giải trình:

  1. $*cũng giống như $@, ngoại trừ việc các tham số không được phân tách bằng dấu cách, mà bằng ký tự đầu tiên của Dấu tách trường nội bộ ( IFS) . Với IFS=+, nó mở rộng thành "$ 1 + $ 2 + ...". Xem sự khác biệt giữa $ * và $ @ là gì?
  2. Chúng tôi đặt IFStrong một khung con (lưu ý các dấu ngoặc đơn xung quanh) để lớp vỏ chính không bị ảnh hưởng. IFStheo mặc định là: \t\n(dấu cách, tab, dòng mới). Đây là một thay thế cho việc sử dụng localcác biến.

Bây giờ để trả lời câu hỏi của bạn:

Bạn có thể sử dụng một giá trị mặc định cho bất kỳ biến hoặc tham số. Hoặc:

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

Hoặc là:

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

6
Tiện lợi! Tôi biết ý kiến ​​không có ý khen ngợi và cảm ơn, nhưng giải pháp này chỉ là ... xấu xa! :-)
zwets

17

Có một cái nhìn vào các shiftnhà điều hành. Nó sẽ chuyển đối số 2 trở đi sang vị trí 1 trở đi, loại bỏ đối số 1.

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

4

Bạn có thể sử dụng một định nghĩa đệ quy chấm dứt khi sumđược gọi mà không có đối số. Chúng tôi sử dụng thực tế là testkhông có đối số đánh giá false.

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

3

Thử đi:

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

Điều này sẽ xuất 12 và 30.

$@đề cập đến tham số, $#trả về số lượng tham số, trong trường hợp này là 3 hoặc 11.

Đã thử nghiệm trên linux redhat 4


2

Bạn chỉ có thể sử dụng một vòng lặp nhỏ:

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

Cá nhân, tôi chỉ sử dụng perlhoặc awkthay vào đó:

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

hoặc là

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

2

Sử dụng 0 làm giá trị mặc định cho $ 1 đến $ 9:

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

Từ 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.

Ví dụ:

$ SUM

Tổng bằng 0

$ SUM 1 2 

Tổng là 3

$ SUM 1 1 1 1 1 1 1 1 1 

Tổng là 9


Cùng một đầu ra với awk:

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

1

Đó cũng là giải pháp của riêng tôi, tôi đã thử và tìm thấy:

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

$ SUM 4 6 5
The sum is 15

Nhưng câu trả lời của @ muru là tốt.


+1: Việc sử dụng hai mức mở rộng số học thú vị để đánh giá các tham số trống bằng không.
muru

1
@muru cảm ơn, nhưng trong trường hợp này câu trả lời của tôi, chúng tôi không sử dụng hơn 9 đối số và chúng tôi phải sử dụng nhóm đối số để vượt qua hơn 9. cảm ơn vì câu trả lời của bạn rất hoàn hảo.
αsнι
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.