การจับคู่ Regex ในคำสั่ง Bash if


89

ฉันทำอะไรผิดที่นี่?

พยายามจับคู่สตริงที่มีช่องว่างตัวพิมพ์เล็กตัวพิมพ์ใหญ่หรือตัวเลข อักขระพิเศษก็น่าจะดีเช่นกัน แต่ฉันคิดว่าต้องมีการหลีกเลี่ยงอักขระบางตัว

TEST="THIS is a TEST title with some numbers 12345 and special char *&^%$#"

if [[ "$TEST" =~ [^a-zA-Z0-9\ ] ]]; then BLAH; fi

เห็นได้ชัดว่านี่เป็นการทดสอบเฉพาะบนล่างตัวเลขและช่องว่าง ใช้งานไม่ได้

* อัพเดท *

ฉันเดาว่าฉันควรจะเจาะจงมากกว่านี้ นี่คือบรรทัดจริงของรหัส

if [[ "$TITLE" =~ [^a-zA-Z0-9\ ] ]]; then RETURN="FAIL" && ERROR="ERROR: Title can only contain upper and lowercase letters, numbers, and spaces!"; fi

* อัพเดท *

./anm.sh: line 265: syntax error in conditional expression
./anm.sh: line 265: syntax error near `&*#]'
./anm.sh: line 265: `  if [[ ! "$TITLE" =~ [a-zA-Z0-9 $%^\&*#] ]]; then RETURN="FAIL" && ERROR="ERROR: Title can only contain upper and lowercase letters, numbers, and spaces!"; return; fi'

จริงๆแล้วคุณใช้เชลล์ตัวไหน? / bin / sh? / bin / ทุบตี? / bin / csh?
Willem Van Onsem

8
การใส่ regex ในตัวแปรจะปลอดภัยกว่า re='...whatever...'; [[ $string =~ $re ]](ไม่มีเครื่องหมายคำพูด - นี่เป็นหนึ่งในกรณีที่หายากที่พวกเขาจะทำลายบางสิ่งที่จะทำงานได้หากไม่มีพวกเขา)
Charles Duffy

3
ใส่เครื่องหมายคำพูดเดียวรอบ ๆ งานแทน เครื่องหมายคำพูดคู่จะไม่ปกป้องอักขระพิเศษอย่างเหมาะสม
tripleee

ขอบคุณมาก Charles! ยังใช้ได้ไม่ต้องใส่ตัวแปร แต่ต้องไม่ยกมาเลย! ตัวอย่างเช่น: [[ $var =~ .* ]]สำหรับการจับคู่ regex .*(อะไรก็ได้) ฉันเดาว่าถ้าคุณใช้เครื่องหมายคำพูดคำพูดนั้นถือเป็นส่วนหนึ่งของนิพจน์ทั่วไป…
Stéphane

4
ฉันพบข้อมูลสรุป gotcha: (1. ) บันทึกรูปแบบในตัวแปรโดยใช้เครื่องหมายคำพูดเดี่ยวpattern='^hello[0-9]*$'(2. ) ในนิพจน์สี่เหลี่ยมคู่หากคุณต้องการการจับคู่ regex อย่าอ้างรูปแบบเนื่องจากการอ้างอิงปิดใช้งานการจับคู่รูปแบบ regex (กล่าวคือนิพจน์[[ "$x" =~ $pattern ]]จะจับคู่โดยใช้ regex และนิพจน์[[ "$x" =~ "$pattern" ]]ปิดใช้งานการจับคู่ regex และเทียบเท่ากับ[[ "$x" == "$pattern" ]] )
Trevor Boyd Smith

คำตอบ:


184

มีสองสิ่งสำคัญที่ควรทราบเกี่ยวกับ[[ ]]โครงสร้างของ bash ครั้งแรก:

แยก Word และการขยายตัวของพา ธ ที่ยังไม่ได้ดำเนินการเกี่ยวกับคำพูดระหว่าง[[และ]]; การขยายตัวทิลเดอพารามิเตอร์และการขยายตัวแปรการขยายเลขคณิตการทดแทนคำสั่งการทดแทนกระบวนการและการลบเครื่องหมายคำพูดจะดำเนินการ

สิ่งที่สอง:

ตัวดำเนินการไบนารีเพิ่มเติม '= ~' พร้อมใช้งาน ... สตริงทางด้านขวาของตัวดำเนินการถือเป็นนิพจน์ทั่วไปแบบขยายและจับคู่ตามนั้น ... ส่วนใดส่วนหนึ่งของรูปแบบอาจถูกยกมาเพื่อบังคับให้จับคู่ เป็นสตริง

ดังนั้น$vในด้านใดด้านหนึ่งของ=~จะถูกขยายเป็นค่าของตัวแปรนั้น แต่ผลลัพธ์จะไม่เป็นการแบ่งคำหรือขยายชื่อพา ธ กล่าวอีกนัยหนึ่งก็คือปลอดภัยอย่างยิ่งที่จะปล่อยให้การขยายตัวแปรโดยไม่ต้องใส่เครื่องหมายอัญประกาศทางด้านซ้ายมือ แต่คุณจำเป็นต้องทราบว่าการขยายตัวแปรจะเกิดขึ้นทางด้านขวามือ

ดังนั้นถ้าคุณเขียน: [[ $x =~ [$0-9a-zA-Z] ]]ที่$0ภายใน regex ทางด้านขวาจะขยายก่อน regex จะตีความซึ่งอาจจะทำให้เกิด regex ล้มเหลวในการรวบรวม (ยกเว้นกรณีที่การขยายตัวของ$0ปลายด้วยตัวเลขหรือเครื่องหมายวรรคตอนสัญลักษณ์ที่มีค่า ASCII น้อยกว่า ตัวเลข) หากคุณอ้างด้านขวามือเช่น[[ $x =~ "[$0-9a-zA-Z]" ]]นั้นด้านขวามือจะถือว่าเป็นสตริงธรรมดาไม่ใช่นิพจน์ทั่วไป (และ$0จะยังขยายได้) สิ่งที่คุณต้องการในกรณีนี้คือ[[ $x =~ [\$0-9a-zA-Z] ]]

ในทำนองเดียวกันนิพจน์ระหว่าง[[และ]]จะแบ่งออกเป็นคำก่อนที่นิพจน์ทั่วไปจะถูกตีความ ดังนั้นช่องว่างในนิพจน์ทั่วไปจึงต้องมีการหลีกเลี่ยงหรือยกมา [[ $x =~ [0-9a-zA-Z\ ] ]]หากคุณต้องการที่จะตรงกับตัวอักษรตัวเลขหรือช่องว่างคุณสามารถใช้: อักขระอื่น ๆ ในทำนองเดียวกันจะต้องถูก Escape เช่น#ซึ่งจะเริ่มแสดงความคิดเห็นหากไม่ได้ยกมา แน่นอนคุณสามารถใส่รูปแบบลงในตัวแปรได้:

pat="[0-9a-zA-Z ]"
if [[ $x =~ $pat ]]; then ...

สำหรับ regexes ที่มีอักขระจำนวนมากซึ่งจะต้องมีการ Escape หรือยกมาเพื่อส่งผ่าน bash's lexer หลายคนชอบสไตล์นี้ แต่ระวัง: ในกรณีนี้คุณไม่สามารถอ้างอิงการขยายตัวแปรได้:

# This doesn't work:
if [[ $x =~ "$pat" ]]; then ...

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

valid='0-9a-zA-Z $%&#' # add almost whatever else you want to allow to the list
if [[ ! $x =~ [^$valid] ]]; then ...

!ลบล้างการทดสอบเปลี่ยนเป็นโอเปอเรเตอร์ "ไม่ตรงกัน" และ[^...]คลาสอักขระ regex หมายถึง "อักขระใด ๆ ที่ไม่ใช่..."

การรวมกันของการขยายพารามิเตอร์และตัวดำเนินการ regex สามารถทำให้ไวยากรณ์นิพจน์ทั่วไปของ bash "เกือบอ่านได้" แต่ก็ยังมี gotcha อยู่บ้าง (ไม่ได้มีเสมอไป?) อย่างหนึ่งคือคุณไม่สามารถใส่]ลงไป$validได้แม้ว่าจะ$validมีการยกมายกเว้นในช่วงเริ่มต้น (นั่นเป็นกฎ regex Posix: ถ้าคุณต้องการที่จะรวม]. ในชั้นเรียนของตัวละครก็ต้องไปที่จุดเริ่มต้น-สามารถไปที่จุดเริ่มต้นหรือจุดสิ้นสุดดังนั้นหากคุณจำเป็นต้องใช้ทั้ง]และ-คุณจะต้องเริ่มต้นด้วย]และจบลงด้วย-, นำไปสู่การ regex ว่า "ฉันรู้ว่าสิ่งที่ฉันทำ" อีโมติคอน: [][-])


6
เพียงแค่ต้องการชี้ให้เห็นว่า"! ~ คือตัวดำเนินการ" ไม่ตรงกัน "นั้นไม่เป็นความจริง ไม่ว่าจะใช้if ! [[ $x =~ $y ]]หรือif [[ ! $x =~ $y ]]
แอลกอฮอล์

shellchecker ไม่เห็นด้วย ...SC2076: Don't quote rhs of =~, it'll match literally rather than as a regex.
Leonardo

4
@leonard: สิ่งนั้นแตกต่างจากคำพูดของฉัน "คุณไม่สามารถอ้างอิงการขยายตัวแปร" และความคิดเห็น "สิ่งนี้ใช้ไม่ได้" อย่างไร ไม่มีความชัดเจนเกี่ยวกับเรื่องนี้?
rici

1
@jinbeomhong: การแสดงออกจะแยกออกเป็นคำตามปกติโดยใช้ช่องว่าง แต่การขยายพารามิเตอร์และคำสั่งไม่ใช่การแบ่งคำ
rici

1
@jinbeomhong: ฉันไม่ได้พูดอะไรที่แตกต่างจากคู่มือทุบตี " คำระหว่าง[[และ]]" จะถูกแยกวิเคราะห์ออกจากข้อความของโปรแกรมเช่นเดียวกับบรรทัดคำสั่งที่แยกวิเคราะห์เป็นคำ แตกต่างจากบรรทัดคำสั่งแม้ว่าคำจะไม่ถูกแยกหลังจากการขยาย
rici

30

เผื่อมีคนอยากได้ตัวอย่างโดยใช้ตัวแปร ...

#!/bin/bash

# Only continue for 'develop' or 'release/*' branches
BRANCH_REGEX="^(develop$|release//*)"

if [[ $BRANCH =~ $BRANCH_REGEX ]];
then
    echo "BRANCH '$BRANCH' matches BRANCH_REGEX '$BRANCH_REGEX'"
else
    echo "BRANCH '$BRANCH' DOES NOT MATCH BRANCH_REGEX '$BRANCH_REGEX'"
fi

13

ฉันต้องการใช้[:punct:]สำหรับสิ่งนั้น นอกจากนี้a-zA-Z09-9อาจเป็นเพียง[:alnum:]:

[[ $TEST =~ ^[[:alnum:][:blank:][:punct:]]+$ ]]

-1

หรือคุณอาจกำลังดูคำถามนี้เพราะคุณบังเอิญพิมพ์ผิดโง่ ๆ เหมือนที่ฉันทำและมีการ = ~ ย้อนกลับเป็น ~ =

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