การเปรียบเทียบสตริงในเชลล์สคริปต์แบบไม่คำนึงถึงขนาดตัวพิมพ์


129

==ผู้ประกอบการจะใช้ในการเปรียบเทียบสองสายในสคริปต์เปลือก อย่างไรก็ตามฉันต้องการเปรียบเทียบสองสตริงโดยไม่สนใจขนาดตัวพิมพ์ทำอย่างไร? มีคำสั่งมาตรฐานสำหรับสิ่งนี้หรือไม่?

คำตอบ:


72

ถ้าคุณทุบตี

str1="MATCH"
str2="match"
shopt -s nocasematch
case "$str1" in
 $str2 ) echo "match";;
 *) echo "no match";;
esac

มิฉะนั้นคุณควรบอกเราว่าคุณใช้เชลล์อะไร

ทางเลือกโดยใช้ awk

str1="MATCH"
str2="match"
awk -vs1="$str1" -vs2="$str2" 'BEGIN {
  if ( tolower(s1) == tolower(s2) ){
    print "match"
  }
}'

32
สำหรับทุกคนที่เปรียบเทียบสตริงโดยใช้ifคำสั่งshoptวิธีการนี้กำหนดให้คุณใช้[[ ]]รูปแบบวงเล็บคู่แบบมีเงื่อนไขแทน[ ]รูปแบบวงเล็บปีกกาเดี่ยว ดูเพิ่มเติม: gnu.org/software/bash/manual/html_node/The-Shopt-Builtin.html
บุคคล

3
คำถามบ่งชี้ว่า==ใช้เพื่อเปรียบเทียบสองสตริง แต่การตอบกลับแสดงการเปรียบเทียบแบบตัวพิมพ์เล็กและตัวพิมพ์ใหญ่โดยใช้caseคำสั่ง ทำให้อุ่นใจที่shoptโซลูชั่นนี้ยังช่วยให้สามารถใช้กรณีตายของ==, =~และผู้ประกอบการเปรียบเทียบสตริงอื่น ๆ
taranaki

7
อาจเป็นเรื่องฉลาดที่จะดำเนินการshopt -u nocasematchหลังจากการเปรียบเทียบเสร็จสิ้นเพื่อเปลี่ยนกลับเป็นค่าเริ่มต้นของ bash
Ohad Schneider

6
ดีที่สุดในการบันทึกและกู้คืนการnocasematchตั้งค่า คว้ามันSHELLNOCASEMATCH=`shopt -p nocasematch`แล้วเปลี่ยนด้วยshopt -s nocasematchและเมื่อเสร็จแล้วให้คืนค่าด้วย$SHELLNOCASEMATCH
Urhixidur

2
ยังดีกว่า: SHELLNOCASEMATCH=$(shopt -p nocasematch; true)เพราะshopt -pจะออกจากรหัส 1 หากไม่ได้ตั้งค่าตัวเลือกและสิ่งนี้อาจทำให้สคริปต์ยกเลิกหากset -eมีผล
พวกเราทุกคนโมนิก้า

158

ใน Bash คุณสามารถใช้การขยายพารามิเตอร์เพื่อแก้ไขสตริงเป็นตัวพิมพ์เล็ก / ตัวพิมพ์ใหญ่ทั้งหมด:

var1=TesT
var2=tEst

echo ${var1,,} ${var2,,}
echo ${var1^^} ${var2^^}

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

37
นี่เป็นสิ่งใหม่ใน Bash 4 หรือไม่ อย่างน้อยใน Bash 3.2.51 (ใช้ใน OS X 10.9) มันไม่ทำงาน - echoคำสั่งแรกส่งผลให้:-bash: ${var1,,}: bad substitution
Felix Rabe

1
การใช้งานการเปรียบเทียบแบบคำนึงถึงขนาดตัวพิมพ์ดังกล่าวมีแนวโน้มที่จะเกิดปัญหาการแปล (เช่นปัญหา Turkish I) ฉันไม่รู้ว่าshopt -s nocasematchจะมีการนำไปใช้อย่างไร แต่โดยปกติแล้วโซลูชัน "ระดับภาษา" นั้นจะจัดการได้อย่างถูกต้อง
Ohad Schneider

5
@Ohad Schneider ให้ชาวเติร์กกังวลเกี่ยวกับปัญหาการแปลภาษาตุรกีฉันแค่ต้องการวิธีจับคู่ที่รวดเร็วและมีประสิทธิภาพและนี่ใช้เวลาเค้กโดย huuuuuge margin
niken

1
ผู้ใช้ macOS อัปเกรด Bash ของคุณ ขอแสดงความนับถือเกินสิบปีโดยจุดนี้ itnext.io/upgrading-bash-on-macos-7138bd1066ba
Jason Carter

114

คำตอบทั้งหมดเหล่านี้ไม่สนใจวิธีที่ง่ายและรวดเร็วที่สุดในการทำเช่นนี้ (ตราบใดที่คุณมี Bash 4):

if [ "${var1,,}" = "${var2,,}" ]; then
  echo ":)"
fi

สิ่งที่คุณทำคือการแปลงทั้งสองสตริงเป็นตัวพิมพ์เล็กและเปรียบเทียบผลลัพธ์


6
มีให้เฉพาะใน Bash 4 หรือใหม่กว่า (เช่นไม่ใช่ใน Mac OS X 10.11)
d4Rk

8
@ d4Rk ซึ่งเป็นสาเหตุที่ประโยคแรกของคำตอบของฉันบอกว่า "ตราบเท่าที่คุณมี Bash 4" ต้องบอกว่า Bash 4 เปิดตัวมาแล้วกว่าเจ็ดปีในขณะที่เขียนความคิดเห็นนี้และการติดตั้ง OS X ของฉันเองก็ใช้งานได้นานมาก
Riot

@Riot ขอโทษไม่ได้สังเกตว่าในตอนแรก ฉันรู้ว่าคุณสามารถติดตั้งทุบตีอะไรก็ได้ที่คุณต้องการบน OS X แต่ค่าเริ่มต้นคือ 3.2 และเนื่องจากสคริปต์ของฉันต้องทำงานบน Mac ที่แตกต่างกันเช่นกันนี่ไม่ใช่ตัวเลือกสำหรับฉันจริงๆ
d4Rk

2
@ d4Rk นั้นเข้าใจได้ - หากคุณต้องการรับประกันการพกพาจริงๆฉันขอแนะนำให้ติดกับมาตรฐานเชลล์ POSIX เนื่องจากคุณไม่รับประกันว่าจะพบ bash รุ่นใด ๆ เลยในบางกรณี
Riot

1
นี่คือสิ่งที่ฉันกำลังมองหา ควรสูงขึ้น =)
Robin Winslow

39

บันทึกสถานะของ nocasematch (ในกรณีที่ฟังก์ชั่นอื่น ๆ นั้นขึ้นอยู่กับการปิดการใช้งาน):

local orig_nocasematch=$(shopt -p nocasematch)
shopt -s nocasematch
[[ "foo" == "Foo" ]] && echo "match" || echo "notmatch"
$orig_nocasematch

หมายเหตุ: ใช้เฉพาะในlocalกรณีที่อยู่ภายในฟังก์ชั่น


4
ดีเพราะcaseข้อความ (รวมถึงคำตอบของ ghostdog) จะทำให้การคลานผิวของฉันอยู่เสมอ
SeldomNeedy

15

วิธีหนึ่งคือการแปลงสตริงทั้งสองเป็นบนหรือล่าง:

test $(echo "string" | /bin/tr '[:upper:]' '[:lower:]') = $(echo "String" | /bin/tr '[:upper:]' '[:lower:]') && echo same || echo different

อีกวิธีหนึ่งคือใช้ grep:

echo "string" | grep -qi '^String$' && echo same || echo different

ฉันใช้trวิธีนี้ในแอพพลิเคชั่นที่ใช้งานนักเทียบท่าขึ้นอยู่กับอัลไพน์ (ซึ่งมีให้shผ่านbusybox) ขอบคุณ.
MXWest

7

สำหรับ korn เชลล์ฉันใช้คำสั่งในตัว typeset (-l สำหรับตัวพิมพ์เล็กและ -u สำหรับตัวพิมพ์ใหญ่)

var=True
typeset -l var
if [[ $var == "true" ]]; then
    print "match"
fi

2
นี่เป็นวิธีที่ดีกว่าในแง่ของประสิทธิภาพกว่าการเริ่มต้น awk หรือกระบวนการอื่น ๆ
Alex

1
ในทุบตีประกาศ -l หรือ -u สามารถใช้ในการตั้งค่าคุณลักษณะ
Bharat

5

ง่ายมากถ้าคุณ fgrep ทำการเปรียบเทียบตัวพิมพ์เล็กและตัวพิมพ์ใหญ่:

str1="MATCH"
str2="match"

if [[ $(fgrep -ix $str1 <<< $str2) ]]; then
    echo "case-insensitive match";
fi

มันจะดีกว่าที่จะใช้if fgrep -qix -- "$str1" <<<"$str2"; thenแทน

3

นี่คือทางออกของฉันโดยใช้ tr:

var1=match
var2=MATCH
var1=`echo $var1 | tr '[A-Z]' '[a-z]'`
var2=`echo $var2 | tr '[A-Z]' '[a-z]'`
if [ "$var1" = "$var2" ] ; then
  echo "MATCH"
fi

3

grepมีการ-iตั้งค่าสถานะซึ่งหมายถึงตัวพิมพ์เล็กและตัวพิมพ์ใหญ่ดังนั้นขอให้มันบอกคุณว่า var2 อยู่ใน var1

var1=match 
var2=MATCH 
if echo $var1 | grep -i "^${var2}$" > /dev/null ; then
    echo "MATCH"
fi

3
จะไม่ทำงานหากมีอักขระพิเศษ regex ใน var2
haridsv

3

สำหรับzshไวยากรณ์แตกต่างกันเล็กน้อย แต่ก็ยังสั้นกว่าคำตอบส่วนใหญ่ที่นี่:

> str1='mAtCh'
> str2='MaTcH'
> [[ "$str1:u" = "$str2:u" ]] && echo 'Strings Match!'
Strings Match!
>

สิ่งนี้จะแปลงทั้งสองสตริงเป็นตัวพิมพ์ใหญ่ก่อนการเปรียบเทียบ


อีกวิธีหนึ่งที่ใช้ประโยชน์จาก zsh globbing flagsซึ่งช่วยให้เราสามารถใช้การจับคู่แบบตัวพิมพ์เล็กและตัวพิมพ์ใหญ่ได้โดยตรงโดยใช้iธง glob:

setopt extendedglob
[[ $str1 = (#i)$str2 ]] && echo "Match success"
[[ $str1 = (#i)match ]] && echo "Match success"


1

ฉันเจอบล็อก / กวดวิชาที่ยอดเยี่ยมนี้ไม่ว่าจะเกี่ยวกับรูปแบบตัวพิมพ์เล็กหรือใหญ่ สามวิธีต่อไปนี้อธิบายด้วยรายละเอียดพร้อมตัวอย่าง:

1.แปลงรูปแบบเป็นตัวพิมพ์เล็กโดยใช้คำสั่งtr

opt=$( tr '[:upper:]' '[:lower:]' <<<"$1" )
case $opt in
        sql)
                echo "Running mysql backup using mysqldump tool..."
                ;;
        sync)
                echo "Running backup using rsync tool..."
                ;;
        tar)
                echo "Running tape backup using tar tool..."
                ;;
        *)
                echo "Other options"
                ;;
esac

2.ใช้ regex พร้อมรูปแบบตัวพิมพ์

opt=$1
case $opt in
        [Ss][Qq][Ll])
                echo "Running mysql backup using mysqldump tool..."
                ;;
        [Ss][Yy][Nn][Cc])
                echo "Running backup using rsync tool..."
                ;;
        [Tt][Aa][Rr])
                echo "Running tape backup using tar tool..."
                ;;
        *)
                echo "Other option"
                ;;
esac

3.เปิดnocasematch

opt=$1
shopt -s nocasematch
case $opt in
        sql)
                echo "Running mysql backup using mysqldump tool..."
                ;;
        sync)
                echo "Running backup using rsync tool..."
                ;;
        tar)
                echo "Running tape backup using tar tool..."
                ;;
        *)
                echo "Other option"
                ;;
esac

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