Bagaimana cara menambahkan variabel dalam bash?

609

Saya telah mencoba untuk meningkatkan variabel numerik menggunakan keduanya var=$var+1dan var=($var+1)tidak berhasil. Variabelnya adalah angka, meskipun bash tampaknya membacanya sebagai string.

Bash versi 4.2.45 (1) -release (x86_64-pc-linux-gnu) di Ubuntu 13.10.

pengguna221744
sumber

Jawaban:

948

Ada lebih dari satu cara untuk meningkatkan variabel dalam bash, tetapi apa yang Anda coba tidak benar.

Anda dapat menggunakan misalnya ekspansi aritmatika :

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

Atau Anda dapat menggunakan let:

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

Lihat juga: http://tldp.org/LDP/abs/html/dblparens.html .

Radu Rădeanu
sumber
31
atau ((++var))atau ((var=var+1))atau ((var+=1)).
gniourf_gniourf
6
Anehnya, var=0; ((var++))mengembalikan kode kesalahan sementara var=0; ((var++)); ((var++))tidak. Ada yang tahu kenapa?
phunehehe
15
@ phunehehe Lihatlah help '(('. Baris terakhir berbunyi:Returns 1 if EXPRESSION evaluates to 0; returns 0 otherwise.
Radu Rădeanu
2
Saya menduga evaluasi nol seperti 1mengapa tip @ gniourf_gniourf termasuk ((++var))tetapi tidak ((var++)).
DreadPirateShawn
4
apakah aman digunakan let var++, tanpa tanda kutip?
wjandrea
161
var=$((var + 1))

Aritmatika dalam bash menggunakan $((...))sintaks.

Paul Tanzini
sumber
9
Jauh lebih baik dari jawaban yang diterima. Hanya dalam ruang 10%, Anda berhasil memberikan contoh yang cukup (satu banyak - sembilan berlebihan sampai saat Anda hanya pamer), dan Anda memberikan kami info yang cukup untuk mengetahui bahwa ((...))itulah kunci untuk menggunakan aritmatika di bash. Saya tidak menyadari bahwa hanya dengan melihat jawaban yang diterima - saya pikir ada seperangkat aturan aneh tentang urutan operasi atau sesuatu yang mengarah ke semua tanda kurung dalam jawaban yang diterima.
ArtOfWarfare
82

Analisis Kinerja dari berbagai opsi

Berkat jawaban Radu Rădeanu yang menyediakan cara-cara berikut untuk meningkatkan variabel dalam bash:

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

Ada beberapa cara lain juga. Misalnya, lihat jawaban lain untuk pertanyaan ini.

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

Memiliki begitu banyak opsi mengarah pada dua pertanyaan ini:

  1. Apakah ada perbedaan kinerja di antara mereka?
  2. Jika demikian, mana yang berkinerja terbaik?

Kode uji kinerja tambahan:

#!/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

Hasil:

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

Kesimpulan:

Tampaknya bash lebih cepat dalam performanya i+=1ketika $idinyatakan sebagai integer. letpernyataan tampaknya sangat lambat, dan exprsejauh ini paling lambat karena bukan builtin.

wjandrea
sumber
Rupanya kecepatan berkorelasi dengan panjang perintah. Saya bertanya-tanya apakah perintah memanggil fungsi yang sama.
MatthewRock
18

Ada juga ini:

var=`expr $var + 1`

Perhatikan spasi dan juga ' tidak '

Sementara jawaban Radu, dan komentarnya, lengkap dan sangat membantu, mereka spesifik bash. Saya tahu Anda secara khusus bertanya tentang bash, tapi saya pikir saya akan menjawab karena saya menemukan pertanyaan ini ketika saya mencari untuk melakukan hal yang sama menggunakan sh di busybox di bawah uCLinux. Portable ini melampaui bash.

tphelican
sumber
1
Anda juga dapat menggunakani=$((i+1))
wjandrea
Jika proses substitusi $(...)tersedia pada shell ini, saya akan merekomendasikan menggunakannya sebagai gantinya.
Radon Rosborough
10

Jika Anda menyatakan $varsebagai bilangan bulat, maka apa yang Anda coba pertama kali sebenarnya akan berfungsi:

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

Referensi: Jenis variabel, Bash Guide for Beginners

Radon Rosborough
sumber
7

Ada satu metode yang hilang dalam semua jawaban - bc

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

bcditentukan oleh standar POSIX , jadi harus ada pada semua versi Ubuntu dan sistem yang mendukung POSIX . The <<<redirection dapat diubah untuk echo "$VAR" | bcuntuk portabilitas, tapi karena pertanyaan bertanya tentang bash- itu OK untuk hanya menggunakan <<<.

Sergiy Kolodyazhnyy
sumber
6

Kode kembali 1masalah hadir untuk semua varian default ( let, (()), dll). Ini sering menyebabkan masalah, misalnya, dalam skrip yang digunakan set -o errexit. Inilah yang saya gunakan untuk mencegah kode kesalahan 1dari ekspresi matematika yang mengevaluasi 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
Juve
sumber
0

Ini harus menjadi cara terburuk untuk menyelesaikan tugas semudah itu, tetapi saya hanya ingin mendokumentasikannya untuk bersenang-senang (kira-kira berlawanan dengan kode golf).

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

atau

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

Serius gunakan salah satu pilihan lain yang jauh lebih baik di sini.

leetbacoon
sumber