เครื่องหมายวงเล็บในคณิตศาสตร์ expr: 3 * (2 + 1)


60

expr ดูเหมือนจะไม่ชอบวงเล็บ (ใช้ในคณิตศาสตร์เพื่อจัดลำดับความสำคัญผู้ประกอบการที่ชัดเจน):

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

วิธีแสดงโอเปอเรเตอร์ที่สำคัญในการทุบตี?

คำตอบ:


40

อีกวิธีในการใช้letbash builtin:

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

บันทึก

ดังที่@ Stéphane Chazelas ชี้ให้เห็นว่าbashคุณควรใช้((...))เลขคณิตมากกว่าexprหรือletเพื่อความชัดเจน

สำหรับการพกพาใช้$((...))เช่นคำตอบ @Bernhard


1
+1 ยิ่งอ่านง่าย! ฉันโพสต์คำถาม + คำตอบของฉันแค่คิดว่ามันจะเป็นประโยชน์สำหรับผู้ใช้งาน Linux ของฉัน แต่ตอนนี้ฉันได้รับประโยชน์มากมายจากคำตอบอื่น ๆ :-)
Nicolas Raoul

3
letมีเหตุผลที่จะใช้ไม่ได้ มันไม่ได้เป็นแบบมาตรฐานหรือพกพาได้มากกว่า(( a = 3 * (2 + 1) ))(ทั้งคู่มาจากkshและมีให้เฉพาะใน ksh, bash และ zsh) และอ่านง่ายหรืออ้างง่าย ใช้a=$((3 * (2 + 1)))เป็นแบบพกพา
Stéphane Chazelas

2
ฉันไม่ได้พูดว่ามันผิดฉันแค่บอกว่าไม่ควรใช้เนื่องจากมีทางเลือกที่ดีกว่า (อันหนึ่งสำหรับความชัดเจน((a = 3 * (2 + 1) ))และอีกอันสำหรับความสะดวกในการพกพาa=$((3 * (2 + 1)))) ดังนั้นจึงไม่เป็นข้อสังเกตสำหรับคุณหรือคำตอบของคุณ และผู้ทำประตูสูงสุด
Stéphane Chazelas

@ StéphaneChazelas: อัปเดตคำตอบของฉัน!
cuonglm

ฉันได้ใช้เสมอหรือa=1 $[a+2] a=1 b=2 $[a+b]เหตุผลของพวกเขาคือหลีกเลี่ยงไวยากรณ์นั้นหรือไม่
Gordon

74

คุณสามารถใช้การขยายเลขคณิตแทนได้

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

exprในความเห็นส่วนตัวของฉันนี้มีลักษณะบิตดีกว่ากว่าการใช้

จาก man bash

การขยายตัว ทางเลขคณิตช่วยให้การประเมินผลของการแสดงออกทางคณิตศาสตร์และการทดแทนของผล รูปแบบสำหรับการขยายเลขคณิตคือ:

         $((expression))

การแสดงออกจะได้รับการปฏิบัติราวกับว่ามันอยู่ในเครื่องหมายคำพูดคู่ แต่เครื่องหมายคำพูดคู่ภายในวงเล็บไม่ได้รับการปฏิบัติเป็นพิเศษ โทเค็นทั้งหมดในนิพจน์ได้รับการขยายพารามิเตอร์การขยายสตริงการทดแทนคำสั่งและการลบเครื่องหมายคำพูด การขยายเลขคณิตอาจซ้อนกัน

การประเมินผลจะดำเนินการตามกฎที่ระบุไว้ด้านล่างภายใต้การประเมิน ARITHMETIC หากการแสดงออกไม่ถูกต้องทุบตีพิมพ์ข้อความที่ระบุความล้มเหลวและไม่มีการทดแทนเกิดขึ้น


1
นอกเหนือจากความสามารถในการอ่านแล้วยังไม่ต้องใช้กระบวนการเพิ่มเติมในการคำนวณทางคณิตศาสตร์ มันจัดการโดยเชลล์เอง
chepner

โปรดทราบว่าในเปลือกหอย POSIX มันเป็นเรื่องของการแยกคำดังนั้นมันเป็นนิสัยที่ดีที่จะอ้างมันในบริบทของรายการ
Stéphane Chazelas

เมื่อฉันลองทำสิ่งนี้ที่ bash shell ฉันจะได้รับ 'ชื่อตัวแปรที่ผิดกฎหมาย "
lordhog

40

ไม่มีเหตุผลที่จะใช้exprสำหรับเลขคณิตใน shell สมัยใหม่

POSIX กำหนด$((...))ผู้ประกอบการขยายตัว ดังนั้นคุณสามารถใช้สิ่งนั้นได้ในเชลล์ที่สอดคล้องกับ POSIX ทั้งหมด ( shของไลต์ยูนิกซ์สมัยใหม่ทั้งหมด, ขีดกลาง, ทุบตี, yash, mksh, zsh, posh, ksh ... )

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

kshยังแนะนำletbuiltin ซึ่งผ่านการแสดงออกทางคณิตศาสตร์ชนิดเดียวกันไม่ขยายไปสู่บางสิ่ง แต่ส่งคืนสถานะออกตามว่านิพจน์แก้ไขเป็น 0 หรือไม่เช่นในexpr:

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

อย่างไรก็ตามในขณะที่การอ้างทำให้มันอึดอัดใจและไม่ชัดเจนมาก (ไม่เท่าที่exprแน่นอน) kshยังแนะนำ((...))รูปแบบทางเลือก:

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

ซึ่งมีความชัดเจนมากขึ้นและควรใช้แทน

letและ((...))มีเฉพาะในksh, และzsh ไวยากรณ์ควรเป็นที่ต้องการถ้าพกพาหอยอื่น ๆ เป็นสิ่งจำเป็นเป็นสิ่งจำเป็นสำหรับเปลือกหอยก่อน POSIX บอร์นเหมือน (ปกติบอร์นเปลือกหรือรุ่นแรกของ Almquist เชลล์)bash$((...))expr

ที่ด้านหน้าไม่ใช่บอร์นมีเชลล์จำนวนหนึ่งที่มีตัวดำเนินการทางคณิตศาสตร์ในตัว:

  • csh/ tcsh(อันที่จริงเชลล์ Unix ตัวแรกมีการประเมินผลทางคณิตศาสตร์ในตัว):

    @ a = 3 * (2 + 1)
  • akanga(ขึ้นอยู่กับrc)

    a = $:'3 * (2 + 1)'
  • ในฐานะที่เป็นบันทึกประวัติศาสตร์รุ่นเดิมของ Almquist shell ที่โพสต์บน usenet ในปี 1989 มีexprbuiltin (รวมกับจริงtest) แต่มันถูกลบออกในภายหลัง


ฉันเรียนรู้สิ่งใหม่ทุกวันจากคุณสเตฟาน ฉันขอขอบคุณความรู้เกี่ยวกับเชลล์ POSIX ของคุณมาก!
MattBianco

แล้วไง: $((a = a*2))ล่ะ
Arthur2e5

ถ้าฉันมีจุดลอย นิพจน์ของฉันคือ = $ ((-14 + 0.2 * (1 + 2 + 3))) โทเค็นข้อผิดพลาดคือ ".2 * (1 + 2 + 3)"
Blaise

@ Baise แล้วคุณต้องใช้เชลล์ที่รองรับคะแนนลอยตัว$((...))เช่น zsh, ksh93 หรือ yash
Stéphane Chazelas

16

exprเป็นคำสั่งภายนอกไม่ใช่ไวยากรณ์เชลล์พิเศษ ดังนั้นถ้าคุณต้องการexprเห็นอักขระพิเศษของเชลล์คุณจำเป็นต้องปกป้องพวกเขาจากการแยกเชลล์โดยการอ้างอิง นอกจากนี้exprต้องการให้แต่ละหมายเลขและโอเปอเรเตอร์ถูกส่งผ่านเป็นพารามิเตอร์แยกต่างหาก ดังนั้น:

expr 3 \* \( 2 + 1 \)

ถ้าคุณกำลังทำงานอยู่บนระบบยูนิกซ์โบราณจากปี 1970 หรือปี 1980 exprมีเหตุผลน้อยมากที่จะใช้ ในสมัยก่อนหอยไม่มีวิธีการคำนวณทางคณิตศาสตร์ในตัวและคุณต้องเรียกexprโปรแกรมอรรถประโยชน์แทน POSIX เชลล์ทั้งหมดมีคณิตศาสตร์ในตัวผ่านทางไวยากรณ์การขยายเลขคณิต

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

การสร้าง$((…))ขยายไปยังผลลัพธ์ของนิพจน์ทางคณิตศาสตร์ (เขียนเป็นทศนิยม) Bash เหมือนกับเชลล์ส่วนใหญ่รองรับเฉพาะเลขคณิตเลขจำนวนเต็ม modulo 2 64 (หรือ modulo 2 32สำหรับ bash เวอร์ชั่นเก่าและเชลล์อื่น ๆ บนเครื่อง 32 บิต)

Bash เสนอไวยากรณ์ความสะดวกสบายเพิ่มเติมเมื่อคุณต้องการทำงานที่ได้รับมอบหมายหรือเพื่อทดสอบว่าการแสดงออกเป็น 0 แต่ไม่สนใจเกี่ยวกับผลลัพธ์ โครงสร้างนี้ยังมีอยู่ใน ksh และ zsh แต่ไม่ได้อยู่ใน SH ธรรมดา

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

นอกจากเลขคณิตเลขจำนวนเต็มแล้วexprยังมีฟังก์ชันการจัดการสตริงอีกจำนวนหนึ่ง สิ่งเหล่านี้ก็ถูกรวมด้วยคุณสมบัติของเปลือกหอย POSIX ยกเว้นหนึ่งข้อ: expr STRING : REGEXPทดสอบว่าสตริงตรงกับ regexp ที่ระบุหรือไม่ POSIX เชลล์ไม่สามารถทำสิ่งนี้ได้หากไม่มีเครื่องมือภายนอก แต่ bash สามารถทำได้[[ STRING =~ REGEXP ]](ด้วยไวยากรณ์ regexp ที่แตกต่างกันexprเป็นเครื่องมือแบบคลาสสิกและใช้ BRE, bash ใช้ ERE)

ถ้าคุณไม่ดูแลสคริปต์ที่ทำงานบนระบบ 20 ปีคุณไม่จำเป็นต้องรู้ว่าexprมีตัวตนอยู่ ใช้เลขคณิตของเชลล์


expr foo : '\(.\)'ยังแยกข้อความ bash's BASH_REMATCHประสบความสำเร็จในสิ่งที่คล้ายกัน นอกจากนี้ยังไม่เปรียบเทียบสตริงซึ่ง POSIX [ไม่ได้ทำ (แม้คนหนึ่งจะคิดวิธีที่จะใช้sortสำหรับที่)
Stéphane Chazelas

ขีดล่างเป็นตัวยึดไวยากรณ์คุณเป็น Schemer, @Giles หรือไม่ :]
RubyTuesdayDONO

1
@RubyTuesdayDONO ฉันไม่ได้ใช้ขีดล่างที่นี่ คุณกำลังอ่านข้อผิดพลาด U + 2026 HORIZONTAL หรือไม่? ถ้าเป็นเช่นนั้นลองใช้แบบอักษรที่ใหญ่ขึ้น
Gilles

@Giles - โอเคใช่มันดูเหมือนขีดเส้นใต้เพราะขนาดตัวอักษรของฉัน สำหรับฉัน "Schemer" เป็นส่วนประกอบและมันก็ไม่เหมือนจุดไข่ปลากับขีดเส้นใต้เปลี่ยนความหมายต่อไป ... ไม่จำเป็นต้องได้รับการต่อว่าต่อขาน: /
RubyTuesdayDONO

12

ใช้วงเล็บด้วยเครื่องหมายคำพูด:

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

คำพูดป้องกันไม่ให้ทุบตีตีความวงเล็บเป็นไวยากรณ์ทุบตี


2
สิ่งที่นิโคลัสอธิบาย แต่ไม่ได้อธิบายก็คือโทเค็นในexprบรรทัดคำสั่งจะต้องคั่นด้วยช่องว่าง ดังนั้น; ตัวอย่างเช่นจะไม่ทำงานexpr 3 "*" "(2" "+" "1)" (นอกจากนี้ BTW คุณอาจไม่จำเป็นต้องพูดถึง+)
G-Man

วงเล็บไม่ใช่คำหลักที่ชอบwhileและ[[มันเป็นไวยากรณ์ หากพวกเขาเป็นคำหลักพวกเขาจะไม่ถูกตีความเช่นนั้นในอาร์กิวเมนต์คำสั่ง คุณต้องมีเครื่องหมายอัญประกาศเพื่อที่ bash จะไม่แยกวิเคราะห์ แต่จะเห็นสตริงตามตัวอักษรแทน
Gilles

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.