Grep: เครื่องหมายดอกจัน (*) ไม่ได้ผลเสมอไป


12

หากฉัน grep เอกสารที่มีสิ่งต่อไปนี้:

ThisExampleString

... สำหรับการแสดงออกThis*Stringหรือ*Stringไม่มีอะไรจะส่งคืน อย่างไรก็ตามThis*ส่งคืนบรรทัดข้างต้นตามที่คาดไว้

การแสดงออกถูกล้อมรอบด้วยเครื่องหมายคำพูดหรือไม่ไม่ต่างกัน

ฉันคิดว่าเครื่องหมายดอกจันระบุอักขระไม่ทราบจำนวนเท่าใด? ทำไมมันใช้งานได้ก็ต่อเมื่อเริ่มแสดงออก? ถ้าเรื่องนี้มีจุดมุ่งหมายพฤติกรรมสิ่งที่ฉันจะใช้แทนการแสดงออกThis*Stringและ*String?


เพราะนั่นไม่ใช่วิธีการทำงานของ regex ... (โดยเฉพาะ: * != any number of unknown characters. อ่านเอกสาร)
njzk2

คำตอบ:


19

เครื่องหมายดอกจันในนิพจน์ทั่วไปหมายถึง "จับคู่องค์ประกอบก่อนหน้า 0 หรือมากกว่า"

ในกรณีเฉพาะของคุณด้วยgrep 'This*String' file.txtคุณพยายามที่จะพูดว่า "เฮ้ grep จับคู่คำกับฉันThiตามด้วยตัวพิมพ์เล็กsศูนย์หรือมากกว่านั้นตามด้วยคำว่าString" พิมพ์เล็กsไม่มีที่ไหนเลยที่จะพบในละเว้นExample grep เหตุThisExampleString

ในกรณีของgrep '*String' file.txtคุณกำลังพูดว่า "grep จับคู่สตริงว่างกับฉัน - ไม่มีอะไรเลย - นำหน้าคำว่าString" แน่นอนว่าไม่ใช่วิธีที่ThisExampleStringควรอ่าน (มีความหมายอื่น ๆ ที่เป็นไปได้ -คุณสามารถลองทำสิ่งนี้โดยใช้หรือไม่-Eตั้งค่าสถานะ แต่ไม่มีความหมายใดที่เหมือนกับสิ่งที่คุณต้องการจริงๆที่นี่)

รู้ว่า.หมายถึง "ใด ๆ ตัวเดียว" grep 'This.*String' file.txtเราสามารถทำเช่นนี้: ตอนนี้คำสั่ง grep จะอ่านมันอย่างถูกต้อง: Thisตามด้วยตัวอักษรใด ๆ (คิดว่ามันเป็นทางเลือกของอักขระ ASCII) ซ้ำจำนวนครั้งใด ๆ Stringตามมาด้วย


6
ในทุบตี (และส่วนใหญ่ของเปลือกหอย Unix) *เป็นตัวละครพิเศษและมันควรจะยกมาหรือหนีตัวอย่างเช่นนี้grep 'This*String' file.txtหรือนี้grep This\*String file.txtจะไม่ต้องแปลกใจโดยผลที่ไม่คาดคิด
pabouk

2
@pabouk ในกระสุน*คือตัวแทน ใน grep *เป็นตัวดำเนินการนิพจน์ทั่วไป ดูunix.stackexchange.com/q/57957/70524
muru

11
pabouk ถูกต้องการขยายชื่อไฟล์เกิดขึ้นก่อนที่คำสั่งจะทำงาน เปรียบเทียบและstrace grep .* file.txt |& head -n 1 strace grep '.*' file.txt |& head -n 1นอกจากนี้จริงgrepทำงานยังมีอักขระ Unicode ใด ๆ (เช่นecho -ne ⇏ | grep ⇏เอาท์พุท)
คอส

1
@Serg: คุณมีชื่อเสียงสูงที่นี่ดังนั้นฉันคิดว่าคุณสังเกตเห็นความหมายของฉันในทันที สหกรณ์ได้ติดแท็กคำถามทุบตีbashดังนั้นผมถือว่าคำสั่งที่กล่าวถึงจะถูกตีความโดย ซึ่งหมายความว่าก่อนbashตีความอักขระพิเศษของมันและหลังจากการขยายตัวที่ดำเนินการทั้งหมดผ่านพารามิเตอร์ไปยังกระบวนการที่เกิดใหม่ ----- ตัวอย่างเช่นคำสั่งนี้ในทุบตี: grep This.\*String file.txtจะวางไข่/bin/grepกับพารามิเตอร์เหล่านี้ 0: grep1: 2:This.*String file.txtโปรดสังเกตว่า Bash ลบเครื่องหมายแบ็กสแลชและการ*ส่งผ่านEscape เดิมนั้นถูกส่งผ่านไปอย่างแท้จริง
pabouk

7
ตลก (และสำหรับการแก้ไขปัญหาน่ารังเกียจสวย :) สิ่งที่เป็นคำสั่งของคุณต้องการจะทำงานได้ตามปกติเพราะส่วนใหญ่อาจจะไม่มีไฟล์ที่ตรงกับการแสดงออกทางสัญลักษณ์แทนเปลือกgrep This.*String file.txt ในกรณีดังกล่าวโดยค่าเริ่มต้นทุบตีจะผ่านการโต้แย้งอย่างแท้จริงรวมทั้งThis.*String *
pabouk

8

*metacharacter ใน BRE 1วินาที, ERE 1และ PCRE 1 s ตรงกับ 0 หรือมากกว่าที่ปรากฏของรูปแบบการจัดกลุ่มก่อนหน้านี้ (ถ้ามีรูปแบบการจัดกลุ่มเป็นก่อน*metacharacter), 0 หรือมากกว่าที่ปรากฏของตัวละครคลาสก่อนหน้า (ถ้าตัวละครคลาสคือ ก่อนหน้า*metacharacter) หรือ 0 หรือมากกว่านั้นเกิดขึ้นของตัวละครก่อนหน้า (ถ้าไม่มีรูปแบบการจัดกลุ่มหรือคลาสตัวละครนำหน้า*metacharacter);

ซึ่งหมายความว่าในThis*Stringรูปแบบการเป็น*metacharacter ไม่ได้นำหน้าด้วยรูปแบบที่จัดกลุ่มหรือคลาส*อักขระ metacharacter จับคู่ 0 หรือมากกว่าของอักขระก่อนหน้า (ในกรณีนี้คือsอักขระ):

% cat infile               
ThisExampleString
ThisString
ThissString
% grep 'This*String' infile
ThisString
ThissString

ในการจับคู่อักขระตั้งแต่ 0 ตัวขึ้นไปคุณต้องการจับคู่.อักขระ metacharacter 0 หรือมากกว่าซึ่งตรงกับอักขระใด ๆ :

% cat infile               
ThisExampleString
% grep 'This.*String' infile
ThisExampleString

เครื่องมือค้นหา*เมตาบอร์กใน BREs และ ERE มักจะ "โลภ" เสมอนั่นคือมันจะจับคู่การแข่งขันที่ยาวที่สุด:

% cat infile
ThisExampleStringIsAString
% grep -o 'This.*String' infile
ThisExampleStringIsAString

นี่อาจไม่ใช่พฤติกรรมที่ต้องการ ในกรณีที่ไม่เป็นเช่นนั้นคุณสามารถเปิดgrepเอนจิ้น PCRE (โดยใช้-Pตัวเลือก) และผนวก?อักขระเมตาอักขระซึ่งเมื่อใส่หลังจาก*และ+อักขระเมตามีผลของการเปลี่ยนความโลภ:

% cat infile
ThisExampleStringIsAString
% grep -Po 'This.*?String' infile
ThisExampleString

1: นิพจน์ทั่วไปพื้นฐาน, นิพจน์ทั่วไปแบบขยายและนิพจน์ปกติที่เข้ากันได้ของ Perl


ขอบคุณสำหรับคำตอบที่ให้ข้อมูลมาก อย่างไรก็ตามฉันเลือกคำตอบที่ต่างออกไปเพราะสั้นกว่าและเข้าใจง่ายกว่า +1 สำหรับการให้รายละเอียดมากมาย
Trae

@ Trae ยินดีต้อนรับคุณ ไม่เป็นไรฉันยอมรับว่าอาจซับซ้อนเกินไปและตั้งสมมติฐานมากเกินไปสำหรับบางคนที่ไม่คุ้นเคยกับหัวข้อ
kos

4

หนึ่งในคำอธิบายพบที่นี่ลิงค์ :

เครื่องหมายดอกจัน " *" ไม่ได้มีความหมายเหมือนกันในนิพจน์ทั่วไปเช่นเดียวกับการใช้สัญลักษณ์แทน มันเป็นตัวดัดแปลงที่ใช้กับอักขระเดี่ยวก่อนหน้าหรือนิพจน์เช่น [0-9] เครื่องหมายดอกจันตรงกับศูนย์หรือมากกว่าสิ่งที่นำหน้า ดังนั้นจึง[A-Z]*ตรงกับจำนวนตัวอักษรตัวพิมพ์ใหญ่รวมถึงไม่มีในขณะที่[A-Z][A-Z]*ตรงกับตัวอักษรตัวพิมพ์ใหญ่หนึ่งตัวหรือมากกว่า


1

*มีความหมายพิเศษทั้งเปลือกglobbingตัวอักษร ( "สัญลักษณ์แทน") และเป็นนิพจน์ปกติmetacharacter คุณต้องใช้เวลาทั้งเข้าบัญชี แต่ถ้าคุณพูดgrepการแสดงออกปกติของคุณแล้วคุณสามารถป้องกันไม่ให้เปลือกจากรักษามันพิเศษและให้แน่ใจว่าจะผ่านมันไปไม่เปลี่ยนแปลง แม้ว่าการจัดเรียงของที่คล้ายกันแนวคิดสิ่งที่หมายถึงเปลือกค่อนข้างแตกต่างจากสิ่งที่มันหมายถึง*grep

ก่อนอื่นเชลล์จะถือว่า*เป็นสัญลักษณ์แทน

คุณพูดว่า:

การแสดงออกถูกล้อมรอบด้วยเครื่องหมายคำพูดหรือไม่ไม่ต่างกัน

ขึ้นอยู่กับว่าไฟล์ใดมีอยู่ในไดเรกทอรีใดก็ตามที่คุณเกิดขึ้นเมื่อคุณรันคำสั่ง สำหรับรูปแบบที่มีตัวคั่นไดเรกทอรี/อาจขึ้นอยู่กับไฟล์ที่มีอยู่ในระบบทั้งหมดของคุณ คุณควรอ้างอิงนิพจน์ปกติสำหรับgrep- และอัญประกาศเดี่ยวมักจะดีที่สุด - เว้นแต่คุณจะแน่ใจว่าคุณโอเคกับการแปลงที่อาจเกิดขึ้นอย่างน่าประหลาดใจเก้าประเภทเชลล์จะทำงานก่อนที่จะดำเนินการgrepคำสั่ง

เมื่อเชลล์พบ*อักขระที่ไม่ได้อ้างถึงมันจะหมายถึง "ศูนย์หรือมากกว่าของอักขระใด ๆ " และแทนที่คำที่มีอยู่ในนั้นด้วยรายการชื่อไฟล์ที่ตรงกับรูปแบบ (ชื่อไฟล์ที่เริ่มต้นด้วยการ.ได้รับการยกเว้น - ถ้ารูปแบบของคุณเองเริ่มต้นด้วย. หรือคุณได้กำหนดค่าเปลือกของคุณจะรวมพวกเขาต่อไป.) นี้เป็นที่รู้จักกันglobbing --and ยังโดยชื่อการขยายตัวชื่อไฟล์และการขยายตัวของพา

โดยgrepปกติแล้วเอฟเฟ็กต์จะเป็นชื่อไฟล์แรกที่จับคู่เป็นนิพจน์ปกติ - แม้ว่าจะเป็นที่ชัดเจนสำหรับผู้อ่านมนุษย์ว่ามันไม่ได้หมายถึงการแสดงออกปกติ - ในขณะที่ชื่อไฟล์อื่น ๆ ที่ระบุโดยอัตโนมัติจากคุณ glob จะถูกนำมาเป็นไฟล์ที่อยู่ภายในเพื่อค้นหาการแข่งขัน (คุณไม่เห็นรายการ - มันถูกส่งผ่านไปยัง opaquely grep) คุณแทบไม่ต้องการให้สิ่งนี้เกิดขึ้น

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

  1. มีไม่มีไฟล์ที่มีชื่อการจับคู่ ... หรือคุณมีคนพิการ globbing ในเปลือกของคุณโดยปกติจะมีหรือเทียบเท่าset -f set -o noglobแต่นี่เป็นเรื่องแปลกและคุณอาจจะรู้ว่าคุณทำมัน

  2. คุณกำลังใช้เชลล์ที่มีพฤติกรรมเริ่มต้นที่จะทิ้งไว้*คนเดียวเมื่อไม่มีชื่อไฟล์ที่ตรงกัน นี่เป็นกรณีใน Bash ซึ่งคุณอาจใช้ แต่ไม่ได้อยู่ในกระสุนสไตล์บอร์นทั้งหมด (พฤติกรรมเริ่มต้นในเชลล์ Zsh ที่เป็นที่นิยมเช่นสำหรับ globs เพื่อ(a) การขยายหรือ(b)ทำให้เกิดข้อผิดพลาด) ... หรือคุณได้เปลี่ยนพฤติกรรมนี้ของเชลล์ของคุณ - วิธีการทำแตกต่างกันไป ข้ามเปลือกหอย

  3. ท่านยังไม่ได้เป็นอย่างอื่นบอกว่าเปลือกของคุณจะอนุญาตให้ globs ถูกแทนที่ด้วยอะไรเมื่อไม่มีไฟล์ที่ตรงกันหรือที่จะล้มเหลวด้วยข้อผิดพลาดในสถานการณ์เช่นนี้ ใน Bash ที่จะทำโดยเปิดใช้งานตัวเลือกnullglobหรือfailglob เปลือกตามลำดับ

บางครั้งคุณสามารถพึ่งพา # 2 และ # 3 แต่คุณสามารถพึ่งพา # 1 ได้ grepคำสั่งที่มีรูปแบบ unquoted ที่ทำงานตอนนี้อาจหยุดการทำงานเมื่อคุณมีไฟล์ที่แตกต่างกันหรือเมื่อคุณเรียกใช้งานจากสถานที่ที่แตกต่างกัน พูดการแสดงออกปกติของคุณและปัญหาจะหายไป

จากนั้นgrepถือว่าคำสั่ง*เป็นปริมาณ

คำตอบอื่น ๆ - เช่นที่โดย Sergiy Kolodyazhnyyและโดย kos --also ยังได้กล่าวถึงประเด็นนี้ของคำถามนี้ในรูปแบบที่แตกต่างกันบ้าง ดังนั้นฉันขอแนะนำให้ผู้ที่ยังไม่ได้อ่านให้ทำก่อนหรือหลังอ่านคำตอบที่เหลือ

สมมติว่า*ไม่ให้มันไป grep - ซึ่งอ้างควร ensure-- grepแล้วจะให้หมายความว่ารายการที่นำหน้ามันอาจเกิดขึ้นจำนวนครั้งใดมากกว่าที่มีจะเกิดขึ้นเพียงครั้งเดียวเท่านั้น มันอาจยังเกิดขึ้นอีกครั้ง หรืออาจจะไม่ปรากฏเลย หรืออาจจะซ้ำ ข้อความที่เหมาะกับความเป็นไปได้เหล่านั้นจะถูกจับคู่

ฉันหมายถึงอะไรโดย "รายการ"

  • เดียวตัวละคร เนื่องจากbตรงกับตัวอักษรb, b*ตรงกับศูนย์หรือมากกว่าbs จึงab*cตรงac, abc, abbc, abbbcฯลฯ

    ในทำนองเดียวกันตั้งแต่.ตรงกับตัวอักษรใด ๆ , .*ตรงกับศูนย์หรือมากกว่าตัวอักษร1จึงa.*cไม้ขีดไฟac, akc, ahjglhdfjkdlgjdfkshlgcแม้acccccchjckhccฯลฯหรือ

  • ตัวละครคลาส นับตั้งแต่[xy]การแข่งขันxหรือy, [xy]*การแข่งขันศูนย์ตัวอักษรหรือมากกว่าที่แต่ละคนเป็นอย่างใดอย่างหนึ่งxหรือyจึงp[xy]*qตรงpq, pxq, pyq, pxxq, pxyq, pyxq, pyyq, pxxxq, pxxyqฯลฯ

    นี้ยังใช้กับรูปแบบการจดชวเลขของตัวละครคลาสชอบ\w, \W, และ\s \Sเนื่องจาก\wตรงกับอักขระคำใด ๆ จึงจับคู่อักขระ\w*ศูนย์หรือมากกว่านั้น หรือ

  • กลุ่ม นับตั้งแต่\(bar\)การแข่งขันbar, \(bar\)*การแข่งขันเป็นศูนย์หรือมากกว่าbars จึงfoo\(bar\)*bazตรงfoobaz, foobarbaz, foobarbarbaz, foobarbarbarbazฯลฯ

    ด้วย-Eหรือ-Pเลือกgrepปฏิบัติต่อการแสดงออกปกติของคุณเป็นEREหรือPCREตามลำดับมากกว่าที่จะเป็นBREแล้วกลุ่มถูกล้อมรอบด้วย( )แทน\( \)ดังนั้นแล้วคุณต้องการใช้(bar)แทน\(bar\)และแทนfoo(bar)bazfoo\(bar\)baz

man grepให้คำอธิบายที่สมเหตุสมผลที่สามารถเข้าถึงได้ของ BRE และไวยากรณ์ ERE ในตอนท้ายรวมถึงการแสดงรายการตัวเลือกบรรทัดคำสั่งทั้งหมดที่grepยอมรับได้ในตอนเริ่มต้น ฉันแนะนำหน้าคู่มือนั้นเป็นแหล่งข้อมูลและยังรวมถึงเอกสาร GNU Grepและเว็บไซต์กวดวิชา / ข้อมูลอ้างอิง (ซึ่งฉันได้เชื่อมโยงกับหน้าเว็บหลายหน้าบน)

สำหรับการทดสอบและการเรียนรู้grepฉันแนะนำให้เรียกมันด้วยรูปแบบ แต่ไม่มีชื่อไฟล์ จากนั้นจะรับอินพุตจากเทอร์มินัลของคุณ ใส่บรรทัด; บรรทัดที่ถูกสะท้อนกลับมาหาคุณคือบรรทัดที่มีข้อความที่ตรงกับรูปแบบของคุณ หากต้องการออกให้กดCtrl+ Dที่จุดเริ่มต้นของบรรทัดซึ่งสัญญาณจะสิ้นสุดการป้อนข้อมูล (หรือคุณสามารถกดCtrl+ Cเช่นเดียวกับโปรแกรมบรรทัดคำสั่งส่วนใหญ่) ตัวอย่างเช่น:

grep 'This.*String'

หากคุณใช้--colorแฟล็กgrepจะเน้นส่วนเฉพาะของบรรทัดที่ตรงกับนิพจน์ปกติของคุณซึ่งมีประโยชน์มากสำหรับทั้งการหาว่านิพจน์ปกติทำอะไรและค้นหาสิ่งที่คุณกำลังมองหาเมื่อคุณทำ โดยค่าเริ่มต้นผู้ใช้ Ubuntu มีนามแฝง Bash ที่ทำให้เกิดgrep --color=autoการเรียกใช้ซึ่งเพียงพอสำหรับวัตถุประสงค์นี้เมื่อคุณเรียกใช้grepจากบรรทัดคำสั่งดังนั้นคุณอาจไม่จำเป็นต้องผ่าน--colorด้วยตนเอง

1 ดังนั้น.*ในการแสดงออกปกติหมาย*ถึงความหมายในเปลือกกลม อย่างไรก็ตามความแตกต่างคือgrepพิมพ์บรรทัดที่มีการจับคู่ของคุณที่ใดก็ได้โดยอัตโนมัติดังนั้นโดยทั่วไปไม่จำเป็นต้องมี.*ที่จุดเริ่มต้นหรือจุดสิ้นสุดของการแสดงออกปกติ

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