ใช้นิพจน์ทั่วไปใน if-condition ใน bash


88

ฉันสงสัยว่ากฎทั่วไปที่จะใช้นิพจน์ทั่วไปใน if clause in bash?

นี่คือตัวอย่าง

$ gg=svm-grid-ch  
$ if [[ $gg == *grid* ]] ; then echo $gg; fi  
svm-grid-ch  
$ if [[ $gg == ^....grid* ]] ; then echo $gg; fi  
$ if [[ $gg == ....grid* ]] ; then echo $gg; fi  
$ if [[ $gg == s...grid* ]] ; then echo $gg; fi  
$   

ทำไมสามคนสุดท้ายถึงไม่ตรงกัน?

หวังว่าคุณจะสามารถให้กฎทั่วไปได้มากที่สุดไม่ใช่แค่ตัวอย่างนี้

คำตอบ:


129

เมื่อใช้รูปแบบลูกโลกเครื่องหมายคำถามจะแสดงถึงอักขระตัวเดียวและเครื่องหมายดอกจันแสดงถึงลำดับของอักขระศูนย์หรือมากกว่า:

if [[ $gg == ????grid* ]] ; then echo $gg; fi

เมื่อใช้นิพจน์ทั่วไปจุดจะแสดงถึงอักขระตัวเดียวและเครื่องหมายดอกจันแทนอักขระนำหน้าเป็นศูนย์หรือมากกว่า ดังนั้น " .*" แทนค่าศูนย์หรือมากกว่าของอักขระใด ๆ " a*" แทนศูนย์หรือมากกว่า "a", " [0-9]*" แทนตัวเลขศูนย์หรือมากกว่า อีกอันที่มีประโยชน์ (ในจำนวนมาก) คือเครื่องหมายบวกซึ่งแสดงถึงอักขระก่อนหน้าอย่างน้อยหนึ่งตัว ดังนั้น " [a-z]+" แทนอักขระอัลฟาตัวพิมพ์เล็กอย่างน้อยหนึ่งตัว (ในภาษา C - และอื่น ๆ อีกบางตัว)

if [[ $gg =~ ^....grid.*$ ]] ; then echo $gg; fi

ดังนั้นจึงมีสองวิธีในการจับคู่สตริง: รูปแบบลูกโลกและนิพจน์ทั่วไป? glob pettern ไม่ได้ใช้สำหรับชื่อไฟล์เท่านั้นหรือไม่? ใน bash ควรใช้รูปแบบ glob เมื่อใดและควรใช้นิพจน์ทั่วไปเมื่อใด ขอบคุณ!
ทิม

1
@Tim: Globbing มีอยู่ใน Bash ส่วนใหญ่หรือทุกเวอร์ชัน การจับคู่ Regex ใช้ได้เฉพาะในเวอร์ชัน 3 ขึ้นไป แต่ฉันขอแนะนำให้ใช้ในเวอร์ชัน 3.2 ขึ้นไปเท่านั้น regexes มีมากมากขึ้นหลากหลายกว่า globbing
Dennis Williamson



8

การเพิ่มโซลูชันนี้ด้วยgrepและshบิวด์อินพื้นฐานสำหรับผู้ที่สนใจในโซลูชันแบบพกพามากขึ้น (ไม่ขึ้นกับbashเวอร์ชันทำงานร่วมกับเวอร์ชันเก่าshบนแพลตฟอร์มที่ไม่ใช่ Linux เป็นต้น)

# GLOB matching
gg=svm-grid-ch    
case "$gg" in
   *grid*) echo $gg ;;
esac

# REGEXP    
if echo "$gg" | grep '^....grid*' >/dev/null ; then echo $gg ; fi    
if echo "$gg" | grep '....grid*' >/dev/null ; then echo $gg ; fi    
if echo "$gg" | grep 's...grid*' >/dev/null ; then echo $gg ; fi    

# Extended REGEXP
if echo "$gg" | egrep '(^....grid*|....grid*|s...grid*)' >/dev/null ; then
  echo $gg
fi    

บางgrepชาติยังรองรับ-qตัวเลือก (เงียบ) เป็นทางเลือกในการเปลี่ยนเส้นทาง/dev/nullแต่การเปลี่ยนเส้นทางเป็นแบบพกพามากที่สุดอีกครั้ง


ลืมปิด ")" สำหรับ egrep
ghostdog74

5
ใช้grep -qแทนgrep >/dev/null.
bfontaine

3

@OP,

glob pettern ไม่ได้ใช้สำหรับชื่อไฟล์เท่านั้นหรือไม่?

ไม่รูปแบบ "glob" ไม่ได้ใช้สำหรับชื่อไฟล์เท่านั้น คุณใช้เพื่อเปรียบเทียบสตริงเช่นกัน ในตัวอย่างของคุณคุณสามารถใช้ case / esac เพื่อค้นหารูปแบบสตริง

 gg=svm-grid-ch 
 # looking for the word "grid" in the string $gg
 case "$gg" in
    *grid* ) echo "found";;
 esac

 # [[ $gg =~ ^....grid* ]]
 case "$gg" in ????grid*) echo "found";; esac 

 # [[ $gg =~ s...grid* ]]
 case "$gg" in s???grid*) echo "found";; esac

ในการทุบตีควรใช้รูปแบบลูกโลกเมื่อใดและควรใช้นิพจน์ทั่วไปเมื่อใด ขอบคุณ!

Regex มีความหลากหลายและ "สะดวก" กว่า "รูปแบบลูกโลก" อย่างไรก็ตามเว้นแต่คุณจะทำงานที่ซับซ้อนซึ่ง "globbing / Extended globbing" ไม่สามารถให้ได้อย่างง่ายดายก็ไม่จำเป็นต้องใช้ regex ไม่รองรับ Regex สำหรับเวอร์ชันของ bash <3.2 (ตามที่เดนนิสกล่าวถึง) แต่คุณยังสามารถใช้ globbing แบบขยายได้ (โดยการตั้งค่าextglob) สำหรับ globbing ขยายให้ดูที่นี่และบางตัวอย่างง่ายๆที่นี่

อัปเดตสำหรับ OP: ตัวอย่างการค้นหาไฟล์ที่ขึ้นต้นด้วยอักขระ 2 ตัว (จุด "." หมายถึงอักขระ 1 ตัว) ตามด้วย "g" โดยใช้ regex

เช่นเอาท์พุท

$ shopt -s dotglob
$ ls -1 *
abg
degree
..g

$ for file in *; do [[ $file =~ "..g" ]] && echo $file ; done
abg
degree
..g

จากด้านบนไฟล์จะตรงกันเนื่องจากชื่อมีอักขระ 2 ตัวตามด้วย "g" (กล่าวคือ..g).

การเทียบเท่ากับ globbing จะเป็นดังนี้: (ดูการอ้างอิงสำหรับความหมายของ?และ*)

$ for file in ??g*; do echo $file; done
abg
degree
..g

ขอบคุณ ghostdog74 ใน Bash ที่มีเวอร์ชันสูงกว่า 3.2 นิพจน์ทั่วไปสามารถใช้เพื่อแทนที่รูปแบบ glob ได้หรือไม่เมื่อใดก็ตามที่ส่วนหลังปรากฏขึ้น หรือนิพจน์ทั่วไปใช้ได้เฉพาะในสถานการณ์พิเศษบางอย่างเท่านั้น? ตัวอย่างเช่นฉันพบว่า "ls ?? g" ทำงานในขณะที่ "ls ..g" ไม่ใช่
ทิม

ไม่มีการหยุดคุณในการใช้ regex หากมีความจำเป็น มันขึ้นอยู่กับคุณ. หมายเหตุไวยากรณ์ regex แตกต่างจากเชลล์ globbing syntax จึงls ..gไม่ทำงาน ..gคุณจะบอกเปลือกที่จะมองหาไฟล์ที่ชื่อ ในฐานะที่เป็นสำหรับการเรียนรู้เกี่ยวกับไวยากรณ์ regex คุณสามารถลองperldoc perlretut, perldoc perlrequickหรือทำinfo sedในบรรทัดคำสั่ง
ghostdog74
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.