Parenthesis dalam expat aritmatika: 3 * (2 + 1)

60

expr tampaknya tidak menyukai tanda kurung (digunakan dalam matematika untuk prioritas operator eksplisit):

expr 3 * (2 + 1)
bash: syntax error near unexpected token `('

Bagaimana cara mengekspresikan prioritas operator dalam bash?

Nicolas Raoul
sumber

Jawaban:

40

Cara lain untuk menggunakan letbash builtin:

$ let a="3 * (2 + 1)"
$ printf '%s\n' "$a"
9

Catatan

Seperti yang ditunjukkan oleh @ Stéphane Chazelas , bashAnda harus menggunakannya ((...))untuk melakukan aritmatika expratau letuntuk keterbacaan.

Untuk portabilitas, gunakan jawaban$((...)) seperti @Bernhard .

cuonglm
sumber
1
+1 Bahkan lebih mudah dibaca! Saya memposting pertanyaan saya + jawaban hanya berpikir itu akan membantu bagi sesama pengguna Linux saya, tetapi sekarang saya mendapatkan banyak manfaat dari jawaban lain :-)
Nicolas Raoul
3
Tidak ada alasan untuk menggunakannya let. Ini tidak lebih standar atau portabel daripada (( a = 3 * (2 + 1) ))(keduanya berasal dari kshdan hanya tersedia dalam ksh, bash dan zsh) dan kurang mudah dibaca atau mudah dikutip. Gunakan a=$((3 * (2 + 1)))untuk menjadi portabel.
Stéphane Chazelas
2
Saya tidak mengatakan itu salah, saya hanya mengatakan itu tidak boleh digunakan karena ada alternatif yang lebih baik (satu untuk keterbacaan ((a = 3 * (2 + 1) )), satu untuk portabilitas a=$((3 * (2 + 1)))), jadi itu bukan catatan terhadap Anda atau jawaban Anda tetapi menentangnya menjadi jawaban yang dipilih dan pencetak gol terbanyak.
Stéphane Chazelas
@ StéphaneChazelas: Memperbarui jawaban saya!
cuonglm
Saya selalu menggunakan a=1 $[a+2]atau a=1 b=2 $[a+b]. Apakah alasan mereka menghindari sintaksis itu?
Gordon
74

Anda dapat menggunakan ekspansi aritmatika saja.

echo "$(( 3 * ( 2 + 1 ) ))"
9

Menurut pendapat pribadi saya, ini terlihat sedikit lebih bagus daripada menggunakan expr.

Dari man bash

Ekspansi Aritmatika Ekspansi aritmatika memungkinkan evaluasi ekspresi aritmatika dan substitusi hasilnya. Format untuk ekspansi aritmatika adalah:

         $((expression))

Ekspresi diperlakukan seolah-olah itu dalam tanda kutip ganda, tetapi tanda kutip ganda di dalam tanda kurung tidak diperlakukan secara khusus. Semua token dalam ekspresi mengalami ekspansi parameter, ekspansi string, penggantian perintah, dan penghapusan kutipan. Ekspansi aritmatika mungkin bersarang.

Evaluasi dilakukan sesuai dengan aturan yang tercantum di bawah ini di bawah EVALUASI ARITHMETIC. Jika ekspresi tidak valid, bash mencetak pesan yang menunjukkan kegagalan dan tidak ada substitusi yang terjadi.

Bernhard
sumber
1
Selain mudah dibaca, itu juga tidak memerlukan forking proses tambahan untuk melakukan aritmatika; itu ditangani oleh shell itu sendiri.
chepner
Perhatikan bahwa dalam shell POSIX, itu dapat dipisahkan kata, jadi itu kebiasaan yang baik untuk mengutipnya dalam konteks daftar.
Stéphane Chazelas
Ketika saya mencoba ini di bash shell saya mendapatkan 'nama variabel ilegal. "
lordhog
40

Tidak ada alasan untuk menggunakan expraritmatika di kulit modern.

POSIX mendefinisikan $((...))operator ekspansi. Jadi Anda dapat menggunakannya di semua shell yang sesuai dengan POSIX (yang paling shdisukai Unix, dash, bash, yash, mksh, zsh, posh, ksh ...).

a=$(( 3 * (2 + 1) ))
a=$((3*(2+1)))

kshjuga memperkenalkan letbuiltin yang dilewatkan dengan jenis ekspresi aritmatika yang sama, tidak berkembang menjadi sesuatu tetapi mengembalikan status keluar berdasarkan apakah ekspresi diselesaikan ke 0 atau tidak, seperti di expr:

if let 'a = 3 * (2 + 1)'; then
  echo "$a is non-zero"
fi

Namun, karena kutipan membuatnya canggung dan tidak terlalu terbaca (tidak pada tingkat yang sama seperti exprtentu saja), kshjuga memperkenalkan ((...))bentuk alternatif:

if (( a = 3 * (2 + 1) )) && (( 3 > 1 )); then
  echo "$a is non-zero and 3 > 1"
fi
((a+=2))

yang jauh lebih mudah dibaca dan harus digunakan sebagai gantinya.

letdan ((...))hanya tersedia di ksh, zshdan bash. The $((...))sintaks harus disukai jika portabilitas untuk kerang lainnya diperlukan, exprhanya diperlukan untuk kerang pre-POSIX Bourne-seperti (biasanya Bourne shell atau versi awal dari Almquist shell).

Di bagian depan non-Bourne, ada beberapa cangkang dengan operator aritmatika bawaan:

  • csh/ tcsh(sebenarnya shell Unix pertama dengan built-in evaluasi aritmatika):

    @ a = 3 * (2 + 1)
  • akanga(berdasarkan rc)

    a = $:'3 * (2 + 1)'
  • sebagai catatan sejarah, versi asli dari shell Almquist, seperti yang diposting di usenet pada tahun 1989 memiliki exprbuiltin (sebenarnya digabung dengan test), tetapi dihapus kemudian.

Stéphane Chazelas
sumber
Saya belajar sesuatu yang baru setiap hari dari Anda, Stéphane. Saya sangat menghargai pengetahuan shell POSIX Anda!
MattBianco
Bagaimana dengan : $((a = a*2))?
Arthur2e5
Bagaimana jika saya memiliki floating point? Ekspresi saya adalah = $ ((-14 + 0,2 * (1 + 2 + 3))). Token kesalahan adalah ".2 * (1 + 2 + 3)"
Blaise
@Blaise, maka Anda akan membutuhkan shell yang mendukung floating point $((...))seperti zsh, ksh93 atau yash.
Stéphane Chazelas
16

expradalah perintah eksternal, itu bukan sintaks shell khusus. Oleh karena itu, jika Anda ingin exprmelihat karakter khusus shell, Anda perlu melindungi mereka dari penguraian shell dengan mengutipnya. Selain itu, exprperlu setiap nomor dan operator untuk dilewatkan sebagai parameter terpisah. Jadi:

expr 3 \* \( 2 + 1 \)

Kecuali jika Anda mengerjakan sistem unix antik dari tahun 1970-an atau 1980-an, hanya ada sedikit alasan untuk menggunakannya expr. Di masa lalu, shell tidak memiliki cara bawaan untuk melakukan aritmatika, dan Anda harus memanggil exprutilitas sebagai gantinya. Semua cangkang POSIX memiliki aritmatika bawaan melalui sintaksis ekspansi aritmatika .

echo "$((3 * (2 + 1)))"

Konstruk $((…))memperluas ke hasil ekspresi aritmatika (ditulis dalam desimal). Bash, seperti kebanyakan shell, hanya mendukung bilangan bulat modulo 2 64 (atau modulo 2 32 untuk versi bash yang lebih lama dan beberapa shell lainnya pada mesin 32-bit).

Bash menawarkan sintaks kenyamanan tambahan ketika Anda ingin melakukan tugas atau untuk menguji apakah ekspresi 0 tetapi tidak peduli dengan hasilnya. Konstruk ini juga ada di ksh dan zsh tetapi tidak di sh polos.

((x = 3 * (2+1)))
echo "$x"
if ((x > 3)); then 

Selain aritmatika integer, exprmenawarkan beberapa fungsi manipulasi string. Ini juga digolongkan oleh fitur-fitur shell POSIX, kecuali untuk satu: expr STRING : REGEXPmenguji apakah string cocok dengan regexp yang ditentukan. Shell POSIX tidak dapat melakukan ini tanpa alat eksternal, tetapi bash can dengan [[ STRING =~ REGEXP ]](dengan sintaksis regexp yang berbedaexpradalah alat klasik dan menggunakan BRE, bash menggunakan ERE).

Kecuali jika Anda mengelola skrip yang berjalan pada sistem berusia 20 tahun, Anda tidak perlu tahu itu exprpernah ada. Gunakan aritmatika shell.

Gilles 'SANGAT berhenti menjadi jahat'
sumber
expr foo : '\(.\)'juga melakukan ekstraksi teks. bash's BASH_REMATCHmencapai sesuatu yang serupa. Ia juga melakukan perbandingan string, yang POSIX [tidak lakukan (walaupun orang bisa membayangkan cara untuk menggunakannya sort).
Stéphane Chazelas
garis bawah sebagai sintaksis placeholder --- Anda seorang Schemer, @Giles? :]
RubyTuesdayDONO
1
@RubyTuesdayDONO Saya tidak menggunakan garis bawah di sini. Apakah Anda salah membaca U + 2026 ELLIPSIS HORIZONTAL? Jika demikian, coba gunakan font yang lebih besar.
Gilles 'SO- stop being evil'
@ Giles - oke, ya, itu hanya terlihat seperti garis bawah karena ukuran font saya. bagi saya, "Schemer" adalah pelengkap, dan itu tidak seperti ellipsis versus garis bawah yang mengubah artinya ... tidak perlu menjadi sombong di atasnya: /
RubyTuesdayDONO
12

Gunakan tanda kurung dengan tanda kutip:

expr 3 '*' '(' 2 '+' 1 ')'
9

Kutipan mencegah bash menafsirkan kurung sebagai sintaks bash.

Nicolas Raoul
sumber
2
Apa yang Nicolas ilustrasikan tetapi tidak jelaskan adalah bahwa token pada exprbaris perintah harus dipisahkan oleh spasi; begitu; misalnya, expr 3 "*" "(2" "+" "1)" tidak akan berfungsi . (Juga, BTW, Anda mungkin tidak perlu mengutipnya +.)
G-Man Mengatakan 'Reinstate Monica'
Tanda kurung tidak seperti kata kunci whiledan [[, itu adalah sintaks. Jika mereka adalah kata kunci, mereka tidak akan ditafsirkan seperti itu dalam argumen perintah. Anda membutuhkan tanda kutip agar bash tidak menguraikannya tetapi sebaliknya melihat string literal.
Gilles 'SANGAT berhenti menjadi jahat'
1

Jika Anda memiliki bc ..

echo '3 * (2 + 1)'|bc 
9                                                                    
rampok
sumber