Câu hỏi Làm thế nào để tăng một biến trong bash?


Tôi đã cố gắng tăng biến số bằng cả hai var=$var+1 và var=($var+1) không thành công. Biến này là một số, mặc dù bash dường như đang đọc nó như một chuỗi.

Bash phiên bản 4.2.45 (1) -release (x86_64-pc-linux-gnu) trên Ubuntu 13.10.


455
2017-12-03 16:34


gốc




Các câu trả lời:


Có nhiều cách để tăng biến trong bash, nhưng những gì bạn đã thử không chính xác.

Bạn có thể sử dụng ví dụ mở rộng số học:

var=$((var+1))
((var=var+1))
((var+=1))
((var++))

Hoặc bạn có thể sử dụng let:

let "var=var+1"
let "var+=1"
let "var++"

Xem thêm: http://tldp.org/LDP/abs/html/dblparens.html.


722
2017-12-03 16:39



hoặc là ((++var)) hoặc là ((var=var+1)) hoặc là ((var+=1)). - gniourf_gniourf
hoặc var = $ (expr $ var + 1) - Javier López
Thật kỳ lạ, var=0; ((var++)) trả về mã lỗi trong khi var=0; ((var++)); ((var++)) không làm. Bất kỳ ý tưởng tại sao? - phunehehe
@phunehehe Nhìn vào help '(('. Dòng cuối cùng nói: Returns 1 if EXPRESSION evaluates to 0; returns 0 otherwise. - Radu Rădeanu
nó có an toàn để sử dụng không let var++, không có dấu ngoặc kép? - wjandrea


var=$((var + 1))

Số học trong sử dụng bash $((...)) cú pháp.


101
2017-12-03 16:38



Vastly tốt hơn so với câu trả lời được chấp nhận. Chỉ với 10% không gian, bạn quản lý để cung cấp đủ ví dụ (một là rất nhiều - chín là quá mức cho đến khi bạn chỉ hiển thị), và bạn cung cấp cho chúng tôi đủ thông tin để biết rằng ((...))là chìa khóa để sử dụng số học trong bash. Tôi không nhận ra rằng chỉ nhìn vào câu trả lời được chấp nhận - tôi nghĩ có một bộ quy tắc kỳ lạ về thứ tự hoạt động hoặc thứ gì đó dẫn đến tất cả dấu ngoặc đơn trong câu trả lời được chấp nhận. - ArtOfWarfare


Phân tích hiệu suất của các tùy chọn khác nhau

Nhờ vào Câu trả lời của Radu Rădeanu cung cấp các cách sau để tăng biến trong bash:

var=$((var+1))
((var=var+1))
((var+=1))
((var++))
let "var=var+1"
let "var+=1" 
let "var++"

Có nhiều cách khác nữa. Ví dụ, hãy xem các câu trả lời khác về câu hỏi này.

let var++
var=$((var++))
((++var))
{
    declare -i var
    var=var+1
    var+=1
}
{
    i=0
    i=$(expr $i + 1)
}

Có quá nhiều tùy chọn dẫn đến hai câu hỏi sau:

  1. Có sự khác biệt về hiệu suất giữa chúng không?
  2. Nếu vậy, cái nào hoạt động tốt nhất?

Mã kiểm tra hiệu suất gia tăng:

#!/bin/bash

# To focus exclusively on the performance of each type of increment
# statement, we should exclude bash performing while loops from the
# performance measure. So, let's time individual scripts that
# increment $i in their own unique way.

# Declare i as an integer for tests 12 and 13.
echo > t12 'declare -i i; i=i+1'
echo > t13 'declare -i i; i+=1'
# Set i for test 14.
echo > t14 'i=0; i=$(expr $i + 1)'

x=100000
while ((x--)); do
    echo >> t0 'i=$((i+1))'
    echo >> t1 'i=$((i++))'
    echo >> t2 '((i=i+1))'
    echo >> t3 '((i+=1))'
    echo >> t4 '((i++))'
    echo >> t5 '((++i))'
    echo >> t6 'let "i=i+1"'
    echo >> t7 'let "i+=1"'
    echo >> t8 'let "i++"'
    echo >> t9 'let i=i+1'
    echo >> t10 'let i+=1'
    echo >> t11 'let i++'
    echo >> t12 'i=i+1'
    echo >> t13 'i+=1'
    echo >> t14 'i=$(expr $i + 1)'
done

for script in t0 t1 t2 t3 t4 t5 t6 t7 t8 t9 t10 t11 t12 t13 t14; do
    line1="$(head -1 "$script")"
    printf "%-24s" "$line1"
    { time bash "$script"; } |& grep user
    # Since stderr is being piped to grep above, this will confirm
    # there are no errors from running the command:
    eval "$line1"
    rm "$script"
done

Các kết quả:

i=$((i+1))              user    0m0.992s
i=$((i++))              user    0m0.964s
((i=i+1))               user    0m0.760s
((i+=1))                user    0m0.700s
((i++))                 user    0m0.644s
((++i))                 user    0m0.556s
let "i=i+1"             user    0m1.116s
let "i+=1"              user    0m1.100s
let "i++"               user    0m1.008s
let i=i+1               user    0m0.952s
let i+=1                user    0m1.040s
let i++                 user    0m0.820s
declare -i i; i=i+1     user    0m0.528s
declare -i i; i+=1      user    0m0.492s
i=0; i=$(expr $i + 1)   user    0m5.464s

Phần kết luận:

Có vẻ như bash là hoạt động nhanh nhất i+=1 khi nào $i được khai báo là số nguyên. let các câu lệnh có vẻ đặc biệt chậm và expr là chậm nhất bởi vì nó không phải là một nội trang.


62
2017-07-31 17:15



Rõ ràng tốc độ tương quan với chiều dài lệnh. Tôi tự hỏi liệu các lệnh gọi các chức năng tương tự. - MatthewRock
i=(expr ...) là lỗi cú pháp. Ý bạn là i=$(expr ...)? - muru
@muru cố định và thêm một kiểm tra vào vòng lặp for. - wjandrea


Ngoài ra còn có điều này:

var=`expr $var + 1`

Ghi chú cẩn thận về không gian và cũng ` không phải là '

Trong khi câu trả lời của Radu, và các bình luận, là đầy đủ và rất hữu ích, chúng đặc biệt bash. Tôi biết bạn đã yêu cầu cụ thể về bash, nhưng tôi nghĩ rằng tôi muốn đường ống kể từ khi tôi tìm thấy câu hỏi này khi tôi đang tìm kiếm để làm điều tương tự bằng cách sử dụng sh trong busybox dưới uCLinux. Điều này di động ngoài bash.


14
2017-08-22 23:11



Bạn cũng có thể dùng i=$((i+1)) - wjandrea
Nếu quá trình thay thế $(...) có sẵn trên trình bao này, tôi khuyên bạn nên sử dụng nó. - Radon Rosborough


Nếu bạn khai báo $var như một số nguyên, thì những gì bạn đã thử lần đầu tiên sẽ thực sự hoạt động:

$ declare -i var=5
$ echo $var
5
$ var=$var+1
$ echo $var
6

Tài liệu tham khảo: Các loại biến, Hướng dẫn Bash cho người mới bắt đầu


9
2017-12-06 22:19





Có một phương pháp mất tích trong tất cả các câu trả lời - bc

$ VAR=7    
$ bc <<< "$VAR+2"
9
$ echo $VAR
7
$ VAR=$( bc <<< "$VAR+1" )
$ echo $VAR
8

bc được chỉ định bởi POSIX tiêu chuẩn, vì vậy phải có mặt trên tất cả các phiên bản của hệ thống tuân thủ Ubuntu và POSIX. Các <<< chuyển hướng có thể được thay đổi thành echo "$VAR" | bc cho tính di động, nhưng vì câu hỏi hỏi về bash - OK khi sử dụng <<<.


6
2018-02-23 13:58





Mã trả lại 1 vấn đề hiện diện cho tất cả các biến thể mặc định (let, (()), v.v.) Điều này thường gây ra sự cố, ví dụ: trong tập lệnh sử dụng set -o errexit. Đây là những gì tôi đang sử dụng để ngăn chặn mã lỗi 1 từ các biểu thức toán học để đánh giá 0;

math() { (( "$@" )) || true; }

math a = 10, b = 10
math a++, b+=2
math c = a + b
math mod = c % 20
echo $a $b $c $mod
#11 12 23 3

4