ฉันจะเขียนโปรแกรมได้อย่างไรถ้าชื่อไฟล์ตรงกับรูปแบบของเชลล์กลม?


13

ผมอยากจะบอกว่าสตริงจะตรงตามรูปแบบ $string glob อาจหรืออาจไม่ใช่ชื่อของไฟล์ที่มีอยู่ ฉันจะทำสิ่งนี้ได้อย่างไร$pattern$string

สมมติรูปแบบต่อไปนี้สำหรับสตริงอินพุตของฉัน:

string="/foo/bar"
pattern1="/foo/*"
pattern2="/foo/{bar,baz}"

ฉันต้องการที่จะหาสำนวนทุบตีที่กำหนดว่า$stringจะได้รับการจับคู่โดย$pattern1, $pattern2หรือรูปแบบ glob พลอื่น ๆ นี่คือสิ่งที่ฉันได้ลองไปแล้ว:

  1. [[ "$string" = $pattern ]]

    เกือบจะใช้งานได้ยกเว้นว่า$patternตีความเป็นรูปแบบสตริงและไม่เป็นรูปแบบแบบกลม

  2. [ "$string" = $pattern ]

    ปัญหาด้วยวิธีนี้คือการที่$patternมีการขยายและจากนั้นเปรียบเทียบสตริงจะดำเนินการระหว่างและการขยายตัวของ$string$pattern

  3. [[ "$(find $pattern -print0 -maxdepth 0 2>/dev/null)" =~ "$string" ]]

    อันนี้ใช้งานได้ แต่เฉพาะถ้า$stringมีไฟล์ที่มีอยู่

  4. [[ $string =~ $pattern ]]

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


2
ปัญหาที่คุณจะพบคือนั่น{bar,baz}ไม่ใช่รูปแบบ มันคือการขยายพารามิเตอร์ แตกต่างที่ลึกซึ้ง แต่ที่สำคัญในการที่{bar,baz}จะขยายตัวมากในช่วงต้นเข้าสู่การขัดแย้งหลายและbar baz
Patrick

หากเชลล์สามารถขยายพารามิเตอร์ได้แน่นอนมันสามารถบอกได้ว่าสตริงนั้นเป็นการขยายที่มีศักยภาพของ glob หรือไม่
jayhendren

ลองทำ a = ls /foo/* ตอนนี้คุณสามารถจับคู่ใน
Hackaholic

1
@ แพทริก: หลังจากอ่านหน้า bash man page ฉันได้เรียนรู้ว่าfoo/{bar,baz}จริงๆแล้วคือการขยายรั้ง (ไม่ใช่การขยายพารามิเตอร์) ในขณะที่foo/*การขยายชื่อพา ธ $stringเป็นการขยายพารามิเตอร์ สิ่งเหล่านี้ทำในเวลาที่ต่างกันและโดยกลไกที่แตกต่างกัน
jayhendren

@jayhendren @Patrick ถูกต้องแล้วคุณได้เรียนรู้ว่าท้ายที่สุดแล้วคำถามของคุณไม่ใช่สิ่งที่ชื่อนำไปสู่ความเชื่อ แต่คุณต้องการจับคู่สตริงกับรูปแบบชนิดต่างๆ หากคุณต้องการจับคู่อย่างเข้มงวดเมื่อเทียบกับรูปแบบ glob caseคำสั่งดำเนินการขยายชื่อพา ธ ("globbing") ตามคู่มือ Bash
Mike S

คำตอบ:


8

ไม่มีวิธีแก้ไขปัญหาทั่วไปสำหรับปัญหานี้ เหตุผลก็คือในการทุบตีการขยายรั้ง (เช่น{pattern1,pattern2,...}และการขยายชื่อไฟล์ (รูปแบบ glob) จะถือว่าเป็นสิ่งที่แยกต่างหากและขยายภายใต้เงื่อนไขที่แตกต่างกันและในช่วงเวลาที่แตกต่างกันนี่คือรายการขยายเต็มรูปแบบที่ทุบตี

  • รั้งการขยายตัว
  • การขยายตัวของตัวหนอน
  • การขยายพารามิเตอร์และตัวแปร
  • การทดแทนคำสั่ง
  • การขยายเลขคณิต
  • การแยกคำ
  • การขยายชื่อพา ธ

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

#!/bin/bash
set -f

string=/foo/bar

for pattern in /foo/{*,foo*,bar*,**,**/*}; do
    [[ $string == $pattern ]] && echo "$pattern matches $string"
done

การรันสคริปต์นี้สร้างผลลัพธ์ต่อไปนี้:

/foo/* matches /foo/bar
/foo/bar* matches /foo/bar
/foo/** matches /foo/bar

งานนี้เพราะset -fการขยายตัวของคนพิการชื่อพา ธ for pattern in /foo/{*,foo*,bar*,**,**/*}เท่านั้นดังนั้นการขยายตัวและการขยายตัวรั้งตัวหนอนเกิดขึ้นในงบ จากนั้นเราสามารถใช้การดำเนิน [[ $string == $pattern ]]การทดสอบเพื่อทดสอบกับการขยายชื่อพา ธ หลังจากการขยายรั้งได้รับการดำเนินการแล้ว


7

ฉันไม่เชื่อว่า{bar,baz} เป็นรูปแบบเปลือกกลม(แต่แน่นอน/foo/ba[rz])แต่ถ้าคุณต้องการทราบว่าการ$stringจับคู่$patternคุณสามารถทำได้:

case "$string" in 
($pattern) put your successful execution statement here;;
(*)        this is where your failure case should be   ;;
esac

คุณสามารถทำได้มากเท่าที่คุณต้องการ:

case "$string" in
($pattern1) do something;;
($pattern2) do differently;;
(*)         still no match;;
esac

1
สวัสดี @mikeserv ตามที่ระบุไว้ในความคิดเห็นและคำตอบที่ฉันให้ไว้ข้างต้นฉันได้เรียนรู้แล้วว่าสิ่งที่คุณพูดเป็นความจริง - {bar,baz}ไม่ใช่รูปแบบกลม ฉันได้คิดวิธีแก้ปัญหาสำหรับคำถามที่คำนึงถึงเรื่องนี้แล้ว
jayhendren

3
+1 คำตอบนี้ตรงกับคำถามที่ให้ไว้ในหัวข้อและประโยคแรก แม้ว่าคำถามต่อมาทำให้เกิดรูปแบบอื่น ๆ ด้วยรูปแบบเปลือกทรงกลม
Mike S

3

ในขณะที่ Patrick ชี้ให้เห็นคุณจำเป็นต้องใช้รูปแบบ "ชนิดที่แตกต่าง":

[[ /foo/bar == /foo/@(bar|baz) ]]


string="/foo/bar"
pattern="/foo/@(bar|baz)"
[[ $string == $pattern ]]

คำพูดไม่จำเป็นต้องมี


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

@ jayhendren จากนั้นคุณอาจต้องแปลงรูปแบบที่เข้ามาเป็น bash เหล่านั้นก่อน
Hauke ​​Laging

ดังนั้นสิ่งที่คุณทำจริง ๆ แล้วก็เปลี่ยนคำถามของฉันจาก "ฉันจะบอกได้อย่างไรว่าชื่อไฟล์เป็นการขยายตัวของการแสดงออก" เป็น "ฉันจะแปลงรูปแบบชื่อไฟล์สไตล์ทุบตีปกติเป็นรูปแบบวงกลมแบบขยายทุบตี"
jayhendren

@jayhendren พิจารณาว่าสิ่งที่คุณต้องการดูเหมือนจะเป็นไปไม่ได้ "สิ่งที่คุณทำจริงๆ" ฟังดูแปลกสำหรับฉัน แต่บางทีมันอาจเป็นเพียงแค่ภาษาต่างประเทศ หากคุณต้องการถามคำถามใหม่นี้คุณต้องอธิบายว่ารูปแบบการป้อนข้อมูลเป็นอย่างไร อาจจะเป็นการsedดำเนินการที่เรียบง่าย
Hauke ​​Laging

1
@HaukeLaging ถูกต้อง ผู้คนที่มาที่นี่เพื่อหาวิธีจับคู่กับรูปแบบ glob (aka "การขยายชื่อพา ธ ") มีแนวโน้มที่จะสับสนเช่นเดียวกับฉันเพราะชื่อกล่าวว่า "รูปแบบเชลล์กลม" แต่เนื้อหาของคำถามของเขาใช้รูปแบบที่ไม่ใช่แบบกลม . เมื่อถึงจุดหนึ่ง jayhendren เรียนรู้เกี่ยวกับความแตกต่าง แต่ความสับสนครั้งแรกของเขาคือสิ่งที่ทำให้ Hauke ​​ตอบวิธีที่เขาทำ
Mike S

1

ฉันจะใช้ grep ดังนี้:

#!/bin/bash
string="/foo/bar"
pattern1="/foo/*"
pattern2="/foo/{bar,baz}"

if echo $string | grep -e "$pattern1" > /dev/null; then
    echo $string matches $pattern1
fi

if echo $string | grep -e "$pattern2" > /dev/null; then
    echo $string matches $pattern2
fi

ซึ่งให้ผลลัพธ์:

./test2.bsh 
/foo/bar matches /foo/*

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