ตัวดำเนินการความเท่าเทียมกันของ Bash (==, -eq)


136

ใครจะกรุณาอธิบายความแตกต่างระหว่าง-eqและ==ในการเขียนสคริปต์ทุบตี?

มีความแตกต่างระหว่างสิ่งต่อไปนี้หรือไม่?

[ $a -eq $b ] และ [ $a == $b ]

มัน==ใช้เฉพาะเมื่อตัวแปรมีตัวเลขหรือไม่?

คำตอบ:


187

เป็นอีกทางหนึ่ง: =และ==ใช้สำหรับการเปรียบเทียบสตริง-eqสำหรับตัวเลข -eqอยู่ในตระกูลเดียวกับ-lt, -le, -gt, -geและ-neหากที่ช่วยให้คุณจำซึ่งเป็นที่

==เป็น bash-ism โดยวิธีการ มันจะดีกว่าที่จะใช้ =POSIX ในการทุบตีทั้งสองมีค่าเท่ากันและในรูปแบบ sh ธรรมดา=เป็นสิ่งเดียวที่รับประกันว่าจะใช้งานได้

$ a=foo
$ [ "$a" = foo ]; echo "$?"       # POSIX sh
0
$ [ "$a" == foo ]; echo "$?"      # bash specific
0
$ [ "$a" -eq foo ]; echo "$?"     # wrong
-bash: [: foo: integer expression expected
2

(หมายเหตุด้านข้าง: อ้างอิงการขยายตัวแปรเหล่านั้นอย่าทิ้งเครื่องหมายคำพูดคู่ไว้ด้านบน)

หากคุณกำลังเขียน#!/bin/bashสคริปต์แล้วผมขอแนะนำให้ใช้[[แทน รูปแบบทวีคูณมีคุณสมบัติมากขึ้นไวยากรณ์ที่เป็นธรรมชาติมากขึ้นและ gotcha น้อยลงที่จะพาคุณไป ไม่จำเป็นต้องมีเครื่องหมายคำพูดคู่อีกต่อไป$aสำหรับหนึ่ง:

$ [[ $a == foo ]]; echo "$?"      # bash specific
0

ดูสิ่งนี้ด้วย:


1
@DJCrashdummy โดย[[รวมแล้วเป็น ksh-ism ยุค 1980 ที่นำมาใช้ (และเปลือกหอยอื่น ๆ อีกมากมาย) นั่นคือจุดรวม - ถ้าคุณมี[[ เลยคุณสามารถสันนิษฐานได้อย่างปลอดภัยว่าส่วนขยายทั้งหมดที่ใช้งานอยู่รอบ ๆ นั้น ( fnmatch()- การจับคู่รูปแบบสไตล์, นิพจน์ทั่วไปของ ERE =~และใช่การปราบปรามการแยกสตริงและการโกลบระบบไฟล์) จะเป็น ใช้ได้ เนื่องจาก[[เป็นไวยากรณ์ที่ไม่ใช่ POSIX โดยสิ้นเชิงจึงไม่มีการสูญเสียความสามารถในการพกพาเพิ่มเติมโดยสมมติว่าคุณลักษณะที่เกิดขึ้นจะพร้อมใช้งาน
Charles Duffy

1
@DJCrashdummy ... ลิงค์ที่คุณแสดงจุดออกได้อย่างถูกต้องว่าคำพูดที่มีความจำเป็นบางครั้งบนด้านขวามือของ[[ $var = $pattern ]]ถ้าคุณต้องการสิ่งที่มิฉะนั้นจะถูกตีความว่าเป็นรูปแบบ fnmatch ให้แทนจะตีความว่าเป็นสตริงตัวอักษร สตริงfooไม่มีการตีความที่ไม่ใช่ตัวอักษรทำให้การออกคำพูดนั้นปลอดภัย เฉพาะในกรณีที่ OP ต้องการจับคู่ให้พูดfoo*(โดยมีเครื่องหมายดอกจันเป็นตัวอักษรไม่ได้หมายถึงสิ่งใดก็ตามที่อยู่หลังสตริงfoo) ที่จะต้องมีเครื่องหมายอัญประกาศหรือการหลีกเลี่ยง
Charles Duffy

@CharlesDuffy น่าเสียดายที่ฉันไม่รู้ว่าคุณหมายถึงอะไรเพราะความคิดเห็นของฉันดูเหมือนจะถูกลบและฉันจำไม่ได้เพราะมันค่อนข้างนานแล้ว :-(
DJCrashdummy

@DJCrashdummy, ... หึฉันคิดว่าคุณจะถอนมันออกไปเอง โดยพื้นฐานแล้วมันเป็นการคัดค้านเกี่ยวกับการขยายที่ไม่[[ได้อ้างถึงภายในเป็นการทุบตี (แสดงว่านั่นเป็นเหตุผลเดียวที่คุณไม่ได้ให้คำตอบ +1)
Charles Duffy

@CharlesDuffy ไม่ไม่ใช่เหตุผลเดียว: นอกเหนือจากความจริงที่ว่าฉันไม่ชอบ bash-isms, ksh-isms ฯลฯ สำหรับงานง่ายๆ (มันทำให้เกิดปัญหาและทำให้ผู้เริ่มต้นสับสน) ฉันไม่เข้าใจว่าทำไมต้องผสม single การจัดฟันและเครื่องหมายเท่ากับคู่[ ... ] ==: - /
DJCrashdummy

27

ขึ้นอยู่กับโครงสร้างการทดสอบรอบตัวดำเนินการ ตัวเลือกของคุณ ได้แก่ วงเล็บปีกกาคู่วงเล็บปีกกาเดี่ยวหรือแบบทดสอบ

หากคุณใช้((... )) แสดงว่าคุณกำลังทดสอบส่วนของเลขคณิตด้วย==ใน C:

$ (( 1==1 )); echo $?
0
$ (( 1==2 )); echo $?
1

(หมายเหตุ: 0หมายถึงtrueในความรู้สึก Unix และไม่ใช่ศูนย์คือการทดสอบล้มเหลว)

การใช้-eqภายในวงเล็บคู่เป็นข้อผิดพลาดทางไวยากรณ์

หากคุณกำลังใช้[... ] (หรือรั้งเดียว) หรือ[[... ]] (หรือรั้งคู่) หรือtestคุณสามารถใช้ -eq, -ne, -lt, -le, -gt หรือ -GE เป็นเปรียบเทียบเลขคณิต

$ [ 1 -eq 1 ]; echo $?
0
$ [ 1 -eq 2 ]; echo $?
1
$ test 1 -eq 1; echo $?
0

==ภายในวงเล็บเดียวหรือสองครั้ง (หรือtestคำสั่ง) เป็นหนึ่งในผู้ประกอบการเปรียบเทียบสตริง :

$ [[ "abc" == "abc" ]]; echo $?
0
$ [[ "abc" == "ABC" ]]; echo $?
1

ในฐานะตัวดำเนินการสตริง=จะเทียบเท่า==และสังเกตช่องว่างรอบ ๆ=หรือที่==ต้องการ

ในขณะที่คุณสามารถทำได้[[ 1 == 1 ]]หรือ[[ $(( 1+1 )) == 2 ]]กำลังทดสอบความเท่าเทียมกันของสตริง - ไม่ใช่ความเท่าเทียมทางคณิตศาสตร์

ดังนั้น-eqการผลิตผลที่ได้อาจจะเป็นที่คาดว่าค่าจำนวนเต็มของ1+1มีค่าเท่ากับ2แม้ว่า RH เป็นสตริงและมีพื้นที่ต่อท้าย:

$ [[ $(( 1+1 )) -eq  "2 " ]]; echo $?
0

ในขณะที่การเปรียบเทียบสตริงของสิ่งเดียวกันจะดึงพื้นที่ต่อท้ายและด้วยเหตุนี้การเปรียบเทียบสตริงจึงล้มเหลว:

$ [[ $(( 1+1 )) ==  "2 " ]]; echo $?
1

และการเปรียบเทียบสตริงที่ผิดอาจทำให้ได้คำตอบที่ผิดทั้งหมด '10' เป็นศัพท์ที่น้อยกว่า '2' ดังนั้นการเปรียบเทียบสตริงจึงส่งกลับtrueหรือ0. หลายคนถูกกัดโดยข้อผิดพลาดนี้:

$ [[ 10 < 2 ]]; echo $?
0

เทียบกับการทดสอบที่ถูกต้องสำหรับ 10 เป็นเลขคณิตน้อยกว่า 2:

$ [[ 10 -lt 2 ]]; echo $?
1

ในความคิดเห็นมีคำถามเกี่ยวกับเหตุผลทางเทคนิคที่ใช้จำนวนเต็ม-eqบนสตริงที่ส่งกลับค่า True สำหรับสตริงที่ไม่เหมือนกัน:

$ [[ "yes" -eq "no" ]]; echo $?
0

เหตุผลก็คือว่าทุบตีuntyped -eqทำให้เกิดสายที่จะตีความว่าเป็นจำนวนเต็มถ้าเป็นไปได้รวมถึงการแปลงฐาน:

$ [[ "0x10" -eq 16 ]]; echo $?
0
$ [[ "010" -eq 8 ]]; echo $?
0
$ [[ "100" -eq 100 ]]; echo $?
0

และ0ถ้า Bash คิดว่ามันเป็นเพียงสตริง:

$ [[ "yes" -eq 0 ]]; echo $?
0
$ [[ "yes" -eq 1 ]]; echo $?
1

ดังนั้น[[ "yes" -eq "no" ]]จะเทียบเท่ากับ[[ 0 -eq 0 ]]

หมายเหตุสุดท้าย: ส่วนขยายเฉพาะ Bash จำนวนมากของ Test Constructs ไม่ใช่ POSIX ดังนั้นจะล้มเหลวในเชลล์อื่น ๆ เปลือกหอยอื่น ๆ โดยทั่วไปจะไม่สนับสนุน[[...]]และ((...))==หรือ


ฉันสงสัยเกี่ยวกับเหตุผลทางเทคนิคในการ[[ "yes" -eq "no" ]]ส่งคืน True bash บังคับให้สตริงเหล่านี้เป็นค่าจำนวนเต็มที่สามารถเปรียบเทียบได้อย่างไร? ;-)
odony

4
ตัวแปรทุบตีมีuntypedเพื่อให้[[ "yes" -eq "no" ]]เทียบเท่ากับ[[ "yes" -eq 0 ]] หรือ[[ "yes" -eq "any_noninteger_string" ]]- ทุกทรู -eqกองกำลังจำนวนเต็มเปรียบเทียบ "yes"ถูกตีความว่าเป็นจำนวนเต็ม0; การเปรียบเทียบเป็นจริงถ้าจำนวนเต็มอื่น ๆ ที่เป็นอย่างใดอย่างหนึ่งหรือผลสตริง0 0
dawg

Boo, ฟ่ออีกครั้ง: แสดง (ไม่สามารถพกพาได้) ==ในตัวอย่างโค้ดและมีเพียงการกล่าวถึง (แบบพกพา, มาตรฐาน) ที่=อยู่ข้างใต้
Charles Duffy

24

==เป็นนามแฝงเฉพาะ bash =และทำการเปรียบเทียบสตริง (ศัพท์) แทนการเปรียบเทียบตัวเลข eqเป็นการเปรียบเทียบตัวเลขแน่นอน

สุดท้ายฉันมักจะชอบใช้แบบฟอร์ม if [ "$a" == "$b" ]


15
การใช้==ที่นี่เป็นรูปแบบที่ไม่ดีตาม=ที่ POSIX ระบุเท่านั้น
Charles Duffy

9
หากคุณจริงๆยืนยันที่จะใช้==แล้วนำมาใส่ในระหว่างและ[[ ]](และตรวจสอบให้แน่ใจว่าบรรทัดแรกของสคริปต์ของคุณระบุให้ใช้/bin/bash)
holgero

18

Guys: คำตอบหลายคำแสดงตัวอย่างอันตราย ตัวอย่างของ OP [ $a == $b ]ใช้การแทนที่ตัวแปรที่ไม่มีเครื่องหมายคำพูดโดยเฉพาะ (แก้ไขเมื่อ ต.ค. 60) เพื่อ[...]ความปลอดภัยสำหรับความเท่าเทียมกันของสตริง

แต่ถ้าคุณจะแจกแจงทางเลือกอื่นเช่น[[...]]คุณต้องแจ้งด้วยว่าต้องยกมาทางขวามือ ถ้าไม่ยกมาแสดงว่าเป็นแพทเทิร์นเป๊ะ! (จากหน้า bash man: "ส่วนใดส่วนหนึ่งของรูปแบบอาจถูกยกมาเพื่อบังคับให้จับคู่เป็นสตริง")

ในการทุบตีสองคำสั่งที่ให้ "ใช่" คือการจับคู่รูปแบบอีกสามรายการคือความเท่าเทียมกันของสตริง:

$ rht="A*"
$ lft="AB"
$ [ $lft = $rht ] && echo yes
$ [ $lft == $rht ] && echo yes
$ [[ $lft = $rht ]] && echo yes
yes
$ [[ $lft == $rht ]] && echo yes
yes
$ [[ $lft == "$rht" ]] && echo yes
$

2
จำเป็นต้อง[ "$lht" = "$rht" ] มีคำพูดที่น่าเชื่อถือแม้เพื่อความเท่าเทียมกัน หากคุณมีแฟ้มที่สร้างขึ้นด้วยtouch 'Afoo -o AB', [ $lft = $rht ]จะกลับจริงแม้ว่าชื่อไฟล์ที่เป็นไม่ได้เลยABที่จะเหมือนกัน
Charles Duffy
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.