Làm cách nào để thêm số trong tập lệnh Bash?


566

Tôi có tập lệnh Bash này và tôi gặp vấn đề ở dòng 16. Làm cách nào tôi có thể lấy kết quả trước đó của dòng 15 và thêm nó vào biến trong dòng 16?

#!/bin/bash

num=0
metab=0

for ((i=1; i<=2; i++)); do
    for j in `ls output-$i-*`; do
        echo "$j"

        metab=$(cat $j|grep EndBuffer|awk '{sum+=$2} END { print sum/120}') (line15)
        num= $num + $metab   (line16)
    done
    echo "$num"
 done

2
Tôi nghĩ bạn sẽ in một giá trị bằng 0 ngay cả khi sử dụng câu trả lời sau. Có một mẹo, ví dụ như trạm biến áp, để đưa giá trị cuối cùng của "bên trong num" thành "bên ngoài num".
Scott Chu

Câu trả lời:


976

Đối với số nguyên :

  • Sử dụng mở rộng số học :$((EXPR))

    num=$((num1 + num2))
    num=$(($num1 + $num2))       # Also works
    num=$((num1 + 2 + 3))        # ...
    num=$[num1+num2]             # Old, deprecated arithmetic expression syntax
  • Sử dụng các exprtiện ích bên ngoài . Lưu ý rằng điều này chỉ cần thiết cho các hệ thống thực sự cũ.

    num=`expr $num1 + $num2`     # Whitespace for expr is important

Đối với điểm nổi :

Bash không hỗ trợ trực tiếp điều này, nhưng có một số công cụ bên ngoài bạn có thể sử dụng:

num=$(awk "BEGIN {print $num1+$num2; exit}")
num=$(python -c "print $num1+$num2")
num=$(perl -e "print $num1+$num2")
num=$(echo $num1 + $num2 | bc)   # Whitespace for echo is important

Bạn cũng có thể sử dụng ký hiệu khoa học (ví dụ 2.5e+2:).


Cạm bẫy thường gặp :

  • Khi đặt một biến, bạn không thể có khoảng trắng ở hai bên =, nếu không, nó sẽ buộc shell phải diễn giải từ đầu tiên là tên của ứng dụng để chạy (ví dụ: num=hoặc num)

    num= 1 num =2

  • bcexprmong đợi mỗi số và toán tử là một đối số riêng biệt, vì vậy khoảng trắng là quan trọng. Họ không thể xử lý các đối số như thế nào 3+ +4.

    num=`expr $num1+ $num2`


1
Trong câu trả lời đầu tiên, nó in cho tôi expr: đối số không phải số thứ hai không hoạt động ..
Nick

1
@Nick: Bạn đã thử điều này trong khi các biến được đặt tên num1num2tồn tại và có các giá trị nguyên?
Bọ Cạp

1
Yew chúng tồn tại nhưng một biến là gấp đôi .. đó có phải là vấn đề không ??
Nick

2
Vâng, đó là một vấn đề :) Lưu ý: việc $((..))đánh giá số học được thực hiện trong bash. exprđược thực hiện như một quy trình riêng biệt, vì vậy nó sẽ chậm hơn rất nhiều. sử dụng cái thứ hai trên các hệ thống mà việc đánh giá số học không được hỗ trợ (sh! = bash)
Karoly Horvath

4
$((…))được chuẩn hóa bởi POSIX, nên nó ngày càng trở nên hiếm hơn exprlà cần thiết.
chepner


30

Có một ngàn lẻ một cách để làm điều đó. Đây là một cách sử dụng dc(một máy tính bàn đánh bóng ngược hỗ trợ số học chính xác không giới hạn):

dc <<<"$num1 $num2 + p"

Nhưng nếu điều đó quá bash-y đối với bạn (hoặc vấn đề về tính di động), bạn có thể nói

echo $num1 $num2 + p | dc

Nhưng có lẽ bạn là một trong những người cho rằng RPN là kỳ lạ và kỳ lạ; đừng lo lắng bcở đây dành cho bạn:

bc <<< "$num1 + $num2"
echo $num1 + $num2 | bc

Điều đó nói rằng, có một số cải tiến không liên quan mà bạn có thể thực hiện cho kịch bản của mình:

#!/bin/bash

num=0
metab=0

for ((i=1; i<=2; i++)); do
    for j in output-$i-* ; do # 'for' can glob directly, no need to ls
            echo "$j"

             # 'grep' can read files, no need to use 'cat'
            metab=$(grep EndBuffer "$j" | awk '{sum+=$2} END { print sum/120}')
            num=$(( $num + $metab ))
    done
    echo "$num"
done

Như được mô tả trong Bash FAQ 022 , Bash không hỗ trợ số dấu phẩy động. Nếu bạn cần tính tổng các số dấu phẩy động, việc sử dụng một công cụ bên ngoài (như bchoặc dc) là bắt buộc.

Trong trường hợp này, giải pháp sẽ là

num=$(dc <<<"$num $metab + p")

Để thêm tích lũy các số có thể nổi vào num.


1
@Sorpigal Thankssss rất nhiều .. Tôi có thể hỏi bạn vài thứ khác không .. làm thế nào tôi có thể in ở cuối không phải là num mà là num / 10 ??
Nick

23

Trong bash

 num=5
 x=6
 (( num += x ))
 echo $num   # ==> 11

Lưu ý rằng bash chỉ có thể xử lý số học số nguyên, vì vậy nếu lệnh awk của bạn trả về một phân số, thì bạn sẽ muốn thiết kế lại: đây là mã của bạn viết lại một chút để làm tất cả toán học trong awk.

num=0
for ((i=1; i<=2; i++)); do      
    for j in output-$i-*; do
        echo "$j"
        num=$(
           awk -v n="$num" '
               /EndBuffer/ {sum += $2}
               END {print n + (sum/120)}
           ' "$j"
        )
    done
    echo "$num"
done

20

Tôi luôn quên cú pháp vì vậy tôi đến google, nhưng sau đó tôi không bao giờ tìm thấy cú pháp tôi quen thuộc: P. Đây là thứ sạch nhất đối với tôi và đúng hơn với những gì tôi mong đợi ở các ngôn ngữ khác.

i=0
((i++))

echo $i;


15

Tôi thực sự thích phương pháp này là tốt, ít lộn xộn:

count=$[count+1]

1
Tại sao điều này làm việc, nhân tiện? Làm thế nào để chúng ta gọi điều này? Tôi không thể tìm thấy tài liệu về những điều này.
đường chân trời7589

4
Ít bừa bộn, nhưng không tán thành.
Camille Goudeseune


7

Một POSIXcách tuân thủ di động khác để thực hiện bash, có thể được định nghĩa là một hàm trong .bashrccho tất cả các toán tử số học về sự thuận tiện.

addNumbers () {
    local IFS='+'
    printf "%s\n" "$(( $* ))"
}

và chỉ gọi nó trong dòng lệnh là,

addNumbers 1 2 3 4 5 100
115

Ý tưởng là sử dụng Bộ phân tách trường đầu vào (IFS) , một biến đặc biệt bashđược sử dụng để phân tách từ sau khi mở rộng và phân tách các dòng thành các từ. Hàm thay đổi giá trị cục bộ để sử dụng ký tự tách từ làm toán tử tổng +.

Hãy nhớ rằng IFSđược thay đổi cục bộ và KHÔNG có hiệu lực đối với IFShành vi mặc định bên ngoài phạm vi chức năng. Một đoạn trích từ man bashtrang,

Shell xử lý từng ký tự của IFS như một dấu phân cách và chia kết quả của các bản mở rộng khác thành các từ trên các ký tự này. Nếu IFS không được đặt hoặc giá trị của nó là chính xác, mặc định, sau đó các chuỗi, và ở đầu và cuối kết quả của các lần mở rộng trước đó sẽ bị bỏ qua và bất kỳ chuỗi ký tự IFS nào không ở đầu hoặc cuối đều phân định từ ngữ.

Biểu "$(( $* ))"diễn danh sách các đối số được truyền để phân chia +và sau đó giá trị tổng là đầu ra sử dụng printfhàm. Hàm này có thể được mở rộng để thêm phạm vi cho các phép toán số học khác.


2
#!/bin/bash

num=0
metab=0

for ((i=1; i<=2; i++)); do      
    for j in `ls output-$i-*`; do
        echo "$j"

        metab=$(cat $j|grep EndBuffer|awk '{sum+=$2} END { print sum/120}') (line15)
        let num=num+metab (line 16)
    done
    echo "$num"
done

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.