Làm thế nào để tăng một biến trong bash?


609

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

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

Câu trả lời:


947

Có nhiều hơn một cách để tăng một biến trong bash, nhưng những gì bạn đã thử là 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 .


31
hoặc ((++var))hoặc ((var=var+1))hoặc ((var+=1)).
gniourf_gniourf

6
Thật kỳ lạ, var=0; ((var++))trả về một mã lỗi trong khi var=0; ((var++)); ((var++))không. Bất cứ ý tưởng tại sao?
phunehehe

15
@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

2
Tôi nghi ngờ việc đánh giá bằng 0 1là tại sao mẹo của @ gniourf_gniourf bao gồm ((++var))nhưng không ((var++)).
DreadPirateShawn

4
Có an toàn để sử dụng let var++, mà không có dấu ngoặc kép?
wjandrea

160
var=$((var + 1))

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


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

82

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

Nhờ câu trả lời của Radu Rădeanu cung cấp các cách sau để tăng một 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ụ, xem các câu trả lời khác cho 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ó rất nhiều lựa chọn dẫn đến hai câu hỏi sau:

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

Mã kiểm tra hiệu suất tăng dần:

#!/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à hiệu suất nhanh nhất i+=1khi $iđược khai báo là số nguyên. letbáo cáo có vẻ đặc biệt chậm và exprcho đến nay là chậm nhất vì nó không phải là nội dung.


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

18

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

var=`expr $var + 1`

Hãy lưu ý cẩn thận các khoảng trắng và ' không phải là '

Mặc dù câu trả lời của Radu và các bình luận là đầy đủ và rất hữu ích, nhưng chúng là đặc trưng của bash. Tôi biết bạn đã hỏi cụ thể về bash, nhưng tôi nghĩ tôi đã tham gia vì tôi đã tìm thấy câu hỏi này khi tôi đang tìm cách 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.


1
Bạn cũng có thể sử dụngi=$((i+1))
wjandrea

Nếu thay thế quá trình $(...)có sẵn trên vỏ này, tôi khuyên bạn nên sử dụng nó thay thế.
Radon Rosborough


7

Có một phương pháp bị thiếu 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 theo tiêu chuẩn POSIX , do đó nên 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 để echo "$VAR" | bccho tính di động, nhưng kể từ khi câu hỏi yêu cầu về bash- đó là OK để chỉ cần sử dụng <<<.


6

Mã trở lại 1vấn đề được trình bày cho tất cả các biến mặc định ( let, (()), vv). Điều này thường gây rắc rối, ví dụ, trong các 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 1từ 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

0

Đây phải là cách tồi tệ nhất để hoàn thành một nhiệm vụ đơn giản như vậy nhưng tôi chỉ muốn ghi lại nó cho vui, tôi đoán (hoàn toàn trái ngược với mã golf).

$ var=0
$ echo $var
0
$ var="$(python -c 'print('$var'+1)')"
$ echo $var
1

hoặc là

$ var="$(printf '%s\n' $var'+1' | bc)"
$ echo $var
1

Nghiêm túc sử dụng một trong những lựa chọn khác tốt hơn nhiều ở đây.

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.