วิธีเปรียบเทียบสตริงใน Bash


958

ฉันจะเปรียบเทียบตัวแปรกับสตริงได้อย่างไร (และทำบางสิ่งถ้าจับคู่กัน)



คำตอบ:


1374

ใช้ตัวแปรในถ้าคำสั่ง

if [ "$x" = "valid" ]; then
  echo "x has the value 'valid'"
fi

หากคุณต้องการที่จะทำบางสิ่งบางอย่างเมื่อพวกเขาไม่ตรงกับแทนที่ด้วย= !=คุณสามารถอ่านเพิ่มเติมเกี่ยวกับการทำงานของสตริงและการดำเนินการทางคณิตศาสตร์ได้ในเอกสารที่เกี่ยวข้อง

ทำไมเราใช้เครื่องหมายคำพูดประมาณ$x?

คุณต้องการเครื่องหมายคำพูดล้อมรอบ$xเพราะถ้ามันว่างเปล่าสคริปต์ Bash ของคุณจะพบข้อผิดพลาดทางไวยากรณ์ดังที่แสดงด้านล่าง:

if [ = "valid" ]; then

การใช้==ตัวดำเนินการที่ไม่ได้มาตรฐาน

โปรดทราบว่าทุบตีอนุญาต==ที่จะใช้เพื่อความเท่าเทียมกันด้วย[แต่ตอนนี้ไม่ได้มาตรฐาน

ใช้กรณีแรกที่มีเครื่องหมายคำพูดล้อมรอบ$xเป็นตัวเลือก:

if [[ "$x" == "valid" ]]; then

หรือใช้กรณีที่สอง:

if [ "$x" = "valid" ]; then

12
ที่เกี่ยวข้องกับคำตอบที่ยอมรับได้รับข้อผิดพลาดในการดำเนินการที่ไม่คาดคิด ? [ "$1" == "on" ]ผมได้รับข้อผิดพลาดเดียวกันเมื่อใช้ การเปลี่ยนสิ่งนี้เป็น ["$ 1" = "เปิด"] แก้ปัญหาได้
Piotr Dobrogost

78
จำเป็นต้องมีช่องว่าง
TAAPSogeking

6
@JohnFeminella เมื่อเขียนสคริปต์ทุบตีมันควรจะมีหนึ่ง=และไม่สอง
user13107

72
มันอาจจะคุ้มค่าที่จะสังเกตว่าคุณไม่สามารถใช้งาน[ $x -eq "valid" ]ได้ -eqเป็นตัวดำเนินการเปรียบเทียบสำหรับจำนวนเต็มไม่ใช่สตริง
craq

2
@Alex ในกรณีใด (ถ้าเคย) ฉันจำเป็นต้องใช้รูปแบบ["x$yes" == "xyes"]ที่นำหน้าทั้งตัวแปรและสตริงตามตัวอักษรด้วยx? นั่นเป็นสิ่งที่ระลึกถึงครั้งเก่าหรือจำเป็นจริงๆในบางสถานการณ์?
lanoxx

142

หรือถ้าคุณไม่ต้องการประโยคอื่น:

[ "$x" == "valid" ] && echo "x has the value 'valid'"

71
และถ้าคุณต้องการประโยคอื่นและต้องการทำให้เป็นหนึ่งซับบ้า: ["$ x" == "ถูกต้อง"] && echo "ถูกต้อง" || echo "invalid"
Matt White

11
@ MattWhite: โดยปกติจะเป็นความคิดที่ไม่ดีเท่าที่echoจะทำได้
gniourf_gniourf

1
แม้สรรพสิ่งที่อาจปรากฏวิธีสวยงามและสง่างามจากทั้ง marko && MattWhite
Deko

3
@gniourf_gniourf [ "$X" == "valid" ] || ( echo invalid && false ) && echo "valid" ไม่มีปัญหาการใช้งาน
12431234123412341234123

4
@ 12431234123412341234123 { echo invalid && false; }นั้นมีประสิทธิภาพมากกว่า( echo invalid && false )เพราะหลีกเลี่ยงการจ่ายเงินสำหรับ subshell ที่ไม่จำเป็น
Charles Duffy

84
a="abc"
b="def"

# Equality Comparison
if [ "$a" == "$b" ]; then
    echo "Strings match"
else
    echo "Strings don't match"
fi

# Lexicographic (greater than, less than) comparison.
if [ "$a" \< "$b" ]; then
    echo "$a is lexicographically smaller then $b"
elif [ "$a" \> "$b" ]; then
    echo "$b is lexicographically smaller than $a"
else
    echo "Strings are equal"
fi

หมายเหตุ:

  1. ช่องว่างระหว่างifและ[และ]มีความสำคัญ
  2. >และ<เป็นตัวดำเนินการเปลี่ยนเส้นทางดังนั้นควรหลีกเลี่ยง\>และ\<ใช้สตริงตามลำดับ

6
ขอบคุณสำหรับการเปรียบเทียบลำดับสตริงตามตัวอักษร
shadi

ปัญหาของฉันคือที่$aจริงแล้ว" "มันล้อมรอบมันเป็นส่วนหนึ่งของค่าตัวอักษรสตริงดังนั้นฉันจึงต้องใช้อักขระตัวหนี$bเพื่อเปรียบเทียบค่า ฉันสามารถค้นหาสิ่งนี้ได้หลังจากทำงานbash -x ./script.shแฟล็ก -x ช่วยให้คุณเห็นคุณค่าของการดำเนินการแต่ละครั้งและช่วยในการดีบัก
ShahNewazKhan

โปรดทราบว่าการเปรียบเทียบลำดับตัวอักษรนั้นไม่ได้มาตรฐาน POSIX ดังนั้นจึงไม่รับประกันว่าจะทำงานกับแพลตฟอร์มที่ไม่ใช่ GNU / เชลล์ที่ไม่ใช้กระสุน เพียง แต่การดำเนินงานที่pubs.opengroup.org/onlinepubs/9699919799/utilities/test.htmlมีการรับประกันที่จะพกพา
Charles Duffy

62

ในการเปรียบเทียบสตริงกับ wildcard ให้ใช้

if [[ "$stringA" == *$stringB* ]]; then
  # Do something here
else
  # Do Something here
fi

12
เป็นสิ่งสำคัญที่ wildcard สามารถใช้ได้ทางด้านขวาเท่านั้น! นอกจากนี้ควรสังเกตสิ่งที่ขาดหายไป"รอบ ๆ สัญลักษณ์แทน (btw: +1 สำหรับสัญลักษณ์ตัวแทน!)
Scz

6
การขยายตัว$stringB จะต้องถูกยกมา if [[ $stringA = *"$stringB"* ]]; then(และบังเอิญทางด้านซ้ายมือไม่จำเป็นที่จะยกมา):
gniourf_gniourf

ฉันพยายามใช้สัญลักษณ์ตัวแทนเดียวกันสำหรับชื่อไฟล์ในพา ธ แต่มันไม่ทำงานสำหรับฉัน ลองใช้สตริงไวด์การ์ดที่แตกต่างกันทั้งหมดที่นี่ แต่มันจะไปที่กรณีอื่นเสมอ stringA ในกรณีของฉันคือเส้นทางของไฟล์ / tmp / file และ stringB คือ "file"
ฝนตก

35

ฉันไม่เห็นด้วยกับหนึ่งในความคิดเห็นในจุดเดียว:

[ "$x" == "valid" ] && echo "valid" || echo "invalid"

ไม่นั่นไม่ใช่คนบ้าออนไลน์

มันเป็นเพียงดูเหมือนว่าหนึ่งถึงอืมไม่ได้ฝึกหัด ...

มันใช้รูปแบบทั่วไปเป็นภาษาในทาง;

และหลังจากที่คุณเรียนภาษา

จริงๆแล้วมันเป็นเรื่องดีที่จะอ่าน

มันคือการแสดงออกทางตรรกะที่เรียบง่ายด้วยส่วนพิเศษหนึ่ง: การประเมินผลที่ขี้เกียจของผู้ประกอบการตรรกะ

[ "$x" == "valid" ] && echo "valid" || echo "invalid"

แต่ละส่วนเป็นการแสดงออกเชิงตรรกะ ครั้งแรกอาจเป็นจริงหรือเท็จอีกสองคนเป็นจริงเสมอ

(
[ "$x" == "valid" ] 
&&
echo "valid"
)
||
echo "invalid"

ตอนนี้เมื่อมีการประเมินผลการตรวจสอบครั้งแรก หากเป็นเท็จมากกว่าตัวถูกดำเนินการที่สองของตรรกะและ &&หลังจากนั้นจะไม่เกี่ยวข้อง คนแรกไม่เป็นความจริงดังนั้นจึงไม่สามารถเป็นคนแรกและคนที่สองได้จริง
ทีนี้ในกรณีนี้คือด้านแรกของตรรกะ หรือ ||เท็จ แต่มันอาจเป็นจริงถ้าอีกด้านหนึ่ง - ส่วนที่สาม - เป็นจริง

ดังนั้นส่วนที่สามจะถูกประเมิน - ส่วนใหญ่เขียนข้อความเป็นผลข้างเคียง (มันมีผล0จริงซึ่งเราไม่ได้ใช้ที่นี่)

อีกกรณีคล้ายกัน แต่ง่ายกว่า - และ - ฉันสัญญา! ง่ายต่อการอ่าน!
(ฉันไม่ได้มี แต่ฉันคิดว่าการเป็นทหารผ่านศึก UNIX ที่มีเคราสีเทาช่วยได้มากในเรื่องนี้)


17
... && ... || ...มักจะขมวดคิ้ว (ขออภัย greybeard Unix เก๋าคุณได้รับไม่ถูกต้องสำหรับทุกเวลานี้) if ... then ... else ...เป็นมันไม่เทียบเท่ากับความหมาย ไม่ต้องกังวลนี้เป็นหลุมพรางที่พบบ่อย
gniourf_gniourf

6
@gniourf_gniourf OP ไม่ผิด - และพวกเขาก็ไม่รู้ตามที่คุณแนะนำ ... && ... || ...เป็นรูปแบบที่ถูกต้องอย่างสมบูรณ์และสำนวนทุบตีทั่วไป การใช้มันจะกำหนดความรู้ก่อนหน้า (ซึ่งอาจเป็นเรื่องดีที่จะทราบถ้ามีผู้เริ่มต้นในผู้ชม) แต่ OP มีผมเพื่อพิสูจน์ว่าพวกเขารู้วิธีที่จะหลีกเลี่ยงครอบคลุมท่อระบายน้ำเปิด
ebpa

3
@ebpa จะเกิดอะไรขึ้นถ้าคำสั่งที่ตามมา && ส่งคืนค่าเท็จจะดำเนินการตามทีโอทีเขาสั่งต่อไปนี้ || ? ถ้าเป็นเช่นนั้นผิดและอาจเป็นสิ่งที่ gniourf แนะนำ
TSG

4
ฉันคิดว่าเสียงสะท้อนเป็นเพียงตัวอย่าง คำสั่งที่ติดตาม && อาจยังส่งคืนค่าที่ไม่เป็นศูนย์
TSG

2
@gniourf_gniourf +1 สำหรับโพสต์ลิงก์ไปยัง Bash Pitfalls! มีประโยชน์มาก!
จากัวร์

21

คุณยังสามารถใช้การใช้เคส / esac

case "$string" in
 "$pattern" ) echo "found";;
esac

ความเท่าเทียมกันนี้มีหรือมี?
ytpillai

@ytpillai มันเทียบเท่ากัน เก็บไว้ในใจคุณสามารถมีรูปแบบแยกจากกันโดยก่อนที่จะ| คำสั่งเทียบเท่ากับในงบ คุณสามารถโต้แย้งว่ามันใช้งานได้กับรายการรูปแบบที่แต่ละรายการมีการประกาศของตัวเองว่าจะทำอย่างไรถ้าคุณมาจาก Python ไม่ชอบแต่ ใช้เป็นคำสั่งสุดท้ายของคุณหากคุณต้องการเงื่อนไข มันกลับมาเมื่อเจอกันครั้งแรก )inthenifsubstring in stringfor item in list*else
mazunki

18

สคริปต์ต่อไปนี้อ่านจากไฟล์ชื่อ "testonthis" ทีละบรรทัดแล้วเปรียบเทียบแต่ละบรรทัดด้วยสตริงแบบง่ายสตริงที่มีอักขระพิเศษและนิพจน์ทั่วไป หากไม่ตรงกันสคริปต์จะพิมพ์บรรทัดมิฉะนั้นจะไม่

ช่องว่างใน Bash นั้นสำคัญมาก ดังนั้นสิ่งต่อไปนี้จะได้ผล:

[ "$LINE" != "table_name" ] 

แต่สิ่งต่อไปนี้จะไม่:

["$LINE" != "table_name"] 

ดังนั้นโปรดใช้ตามที่เป็น:

cat testonthis | while read LINE
do
if [ "$LINE" != "table_name" ] && [ "$LINE" != "--------------------------------" ] && [[ "$LINE" =~ [^[:space:]] ]] && [[ "$LINE" != SQL* ]]; then
echo $LINE
fi
done

ใช้วิธีนี้ในการอ่านไฟล์ นั่นคือลบ UUoC เหนือสิ่งอื่นใด
fedorqui 'ดังนั้นหยุดการทำร้าย'

มันไม่สำคัญเพราะbashแต่[เป็นเพราะจริง ๆ แล้วเป็นเลขฐานสองภายนอก (เช่นเดียวกับที่which [ให้ผลเช่นนี้/usr/bin/[)
Patrick Bergner

11

ฉันอาจจะใช้การแข่งขัน regexp ถ้าอินพุตมีรายการที่ถูกต้องเพียงไม่กี่ เช่นเฉพาะ "เริ่มต้น" และ "หยุด" เป็นการกระทำที่ถูกต้อง

if [[ "${ACTION,,}" =~ ^(start|stop)$ ]]; then
  echo "valid action"
fi

โปรดทราบว่าฉันพิมพ์ตัวพิมพ์เล็ก$ACTIONโดยใช้เครื่องหมายจุลภาคสองตัว นอกจากนี้โปรดทราบว่าวิธีนี้จะไม่สามารถใช้ได้กับเวอร์ชันทุบตีที่เก่าเกินไป


9

Bash 4+ ตัวอย่าง หมายเหตุ: การไม่ใช้เครื่องหมายคำพูดจะทำให้เกิดปัญหาเมื่อคำต่างๆมีช่องว่าง ฯลฯ อ้างถึงใน Bash, IMO เสมอ

นี่คือตัวอย่างบางส่วนใน Bash 4+:

ตัวอย่างที่ 1 ตรวจสอบว่า 'ใช่' ในสตริง (ไม่คำนึงถึงขนาดตัวพิมพ์):

    if [[ "${str,,}" == *"yes"* ]] ;then

ตัวอย่างที่ 2 ตรวจสอบว่า 'ใช่' ในสตริง (ไม่คำนึงถึงขนาดตัวพิมพ์):

    if [[ "$(echo "$str" | tr '[:upper:]' '[:lower:]')" == *"yes"* ]] ;then

ตัวอย่างที่ 3 ตรวจสอบว่า 'ใช่' ในสตริง (เล็กหรือใหญ่):

     if [[ "${str}" == *"yes"* ]] ;then

ตัวอย่างที่ 4 ตรวจสอบว่า 'ใช่' ในสตริง (เล็กหรือใหญ่):

     if [[ "${str}" =~ "yes" ]] ;then

ตัวอย่างที่ 5 การจับคู่แบบตรงทั้งหมด (ตัวพิมพ์เล็กและใหญ่):

     if [[ "${str}" == "yes" ]] ;then

ตัวอย่างที่ 6 การจับคู่แบบตรงทั้งหมด (ตัวพิมพ์เล็กและตัวพิมพ์ใหญ่):

     if [[ "${str,,}" == "yes" ]] ;then

ตัวอย่างที่ 7 การจับคู่แบบตรงทั้งหมด:

     if [ "$a" = "$b" ] ;then

สนุก.


สำหรับฉัน (mac GNU ทุบตีรุ่น 4.4.12 (1) - ปล่อย x86_64-apple-darwin17.0.0) ฉันต้องใช้if [ "$a"="$b" ]หรือไม่ทำงาน ... ไม่สามารถมีช่องว่างรอบเท่ากับ
สเปกตรัม

1

ฉันทำในลักษณะที่เข้ากันได้กับ Bash และDash (sh):

testOutput="my test"
pattern="my"

case $testOutput in (*"$pattern"*)
    echo "if there is a match"
    exit 1
    ;;
(*)
   ! echo there is no coincidence!
;;esac

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