ประวัติของทุบตี Bash


11

มีเหตุผลทางประวัติศาสตร์หรือไม่ทำไม Bash "globbing" และการแสดงออกปกติไม่เหมือนกัน? ตัวอย่างเช่นฉันเชื่อว่าใน Bash [1-2]*ตรงกับสิ่งที่เริ่มต้นด้วย 1 หรือ 2 ตามด้วยสิ่งอื่นในขณะที่นิพจน์ทั่วไป[1-2]*จะจับคู่เฉพาะลำดับที่ 1 และ 2 การเขียนสคริปต์ Bash และ REGEX ของฉันนั้นค่อนข้างอ่อนแอและฉันพบปัญหาที่เกี่ยวข้องกับความแตกต่างเหล่านี้เป็นประจำซึ่งทำให้ฉันสงสัยว่าทำไมพวกเขาถึงแตกต่างกัน


3
คุณจะพิจารณาทำrm -- ^[^.].*\.txt$แทนrm -- *.txtหรือไม่
Stéphane Chazelas

1
ส่วนใหญ่ของ Q ของคุณถูกสัมผัสในหัวข้อนี้จาก lwn: lwn.net/Articles/96687
slm

มีคำสั่งที่ทำงานกับชื่อไฟล์และรับ regexp ยกตัวอย่างเช่นการค้นหาfind . -regex ".*\.txt$" | xargs rm --หรือrenameสำหรับการเปลี่ยนชื่อไฟล์ (มันเป็นsedชื่อไฟล์) renameระวังระบบบางรายที่มีแตกต่างกัน
ctrl-alt-delor

@ Richard, ฉัน^[^.].*\.txt$ต้องคำนึงถึงการไม่สนใจไฟล์ dot โปรดทราบว่า-regexเป็นส่วนขยายของ GNU เปลือกหอยบางอย่างเช่น ksh93 หรือ zsh สามารถรวม regexps ใน globs ของพวกเขา (ลองเช่น: ksh93 -c 'echo ~(E:^[^.].*\.txt$)')
Stéphane Chazelas

2
การทุบตีนั้นปฏิบัติตามแนวทางปฏิบัติที่มีอยู่อย่างระมัดระวังในขณะที่หลีกเลี่ยงการเปลี่ยนแปลงอย่างไม่ลงรอยกันและส่วนขยายที่เข้ากันไม่ได้อย่างถาวรเป็นหนึ่งในจุดแข็งที่ยิ่งใหญ่ที่สุด
ormaaj

คำตอบ:


12

bashได้รับการออกแบบเริ่มแรกในปลาย 80s เป็นโคลนบางส่วนของkshด้วยคุณสมบัติการโต้ตอบบางอย่างจาก csh / tcsh

ต้นกำเนิดของการเกาะเป็นวงกลมจะต้องพบในเปลือกหอยก่อนหน้านี้ที่มันสร้างขึ้น

kshตัวเองเป็นส่วนขยายของเชลล์เป้าหมาย เชลล์เป้าหมายตัวเอง (เปิดตัวครั้งแรกในปี 1979 ใน Unix V7) เป็นการใช้งานที่สะอาดตั้งแต่เริ่มต้น แต่มันไม่ได้แยกออกจากเชลล์ ธ อมป์สัน (เปลือกของ V1 -> V6) และรวมคุณสมบัติต่างๆจากเปลือก Mashey

โดยเฉพาะอย่างยิ่งอาร์กิวเมนต์คำสั่งยังคงถูกคั่นด้วยช่องว่าง|ตอนนี้เป็นตัวดำเนินการไปป์ใหม่ แต่^ยังได้รับการสนับสนุนเป็นทางเลือก (และยังอธิบายถึงสาเหตุที่คุณทำ[!a-z]และไม่[^a-z]) $1ยังคงเป็นอาร์กิวเมนต์แรกสำหรับสคริปต์และแบ็กสแลช . ตัวดำเนินการ regexp จำนวนมาก ( ^\|$) มีความหมายพิเศษของตัวเองในเชลล์

เชลล์ ธ อมป์สันอาศัยเครื่องมือภายนอกเพื่อทำให้กลมกลืน เมื่อshพบ unquoted *, [หรือในคำสั่งก็จะเรียกใช้คำสั่งผ่าน?glob

rm *.txt

จะจบลงด้วยการทำงานแบบ glob เมื่อ:

["glob", "rm", "*.txt"]

และ glob จะจบลงrmด้วยการเรียกใช้รายการไฟล์ที่ตรงกับรูปแบบนั้น

grep a.\*b *.txt

จะทำงานglobเป็น:

["glob", "grep", "a.\252b", "*.txt"]

*ดังกล่าวข้างต้นได้รับการยกโดยการตั้งค่าบิตที่ 8 ได้ที่ตัวละครที่การป้องกันglobจากการรักษามันเป็นสัญลักษณ์แทน แล้วจะเอาบิตที่ก่อนที่จะเรียกglobgrep

หากต้องการทำเทียบเท่ากับ regexps ที่จะได้รับ:

regexp rm '\.txt$'

หรือ:

regexp rm '^[^.].*\.txt$'

เพื่อยกเว้นจุดไฟล์

ความต้องการที่จะหลบหนีโอเปอเรเตอร์ขณะที่พวกเขาเพิ่มเป็นสองเท่าของอักขระพิเศษของเชลล์ความจริงที่ว่า.ชื่อไฟล์ทั่วไปเป็นโอเปอเรเตอร์ regexp ทำให้มันไม่เหมาะที่จะจับคู่ชื่อไฟล์และซับซ้อนสำหรับผู้เริ่มต้น ในกรณีส่วนใหญ่สิ่งที่คุณต้องมีคืออักขระตัวแทนที่สามารถแทนที่หนึ่ง ( ?) หรือจำนวนใดก็ได้ ( *) ของอักขระ

ตอนนี้กระสุนที่แตกต่างกันได้เพิ่มโอเปอเรเตอร์ต่าง ๆ ทุกวันนี้ ksh และ zsh globs (และบางส่วนbash -O extglobที่ใช้ชุดย่อยของ ksh globs) มีหน้าที่เทียบเท่ากับ regexps ที่มีไวยากรณ์ที่ยุ่งยากน้อยกว่าที่จะใช้กับชื่อไฟล์และไวยากรณ์เชลล์ปัจจุบัน ตัวอย่างเช่นในzsh(ด้วยส่วนขยาย Extendedglob) คุณสามารถทำได้:

echo a#.txt

ถ้าคุณต้องการที่ (น่า) เพื่อให้ตรงกับชื่อไฟล์ที่ประกอบด้วยลำดับตามมาด้วยa .txtง่ายกว่าecho (^a*\.txt$)(ที่นี่ใช้วงเล็บปีกกาเป็นวิธีแยกตัวดำเนินการ regex ออกจากตัวดำเนินการเชลล์ซึ่งอาจเป็นวิธีหนึ่งที่เชลล์สามารถจัดการกับมันได้)

echo (foo|bar|<1-20>).(#i)mpg

สำหรับไฟล์ mpg (ตัวพิมพ์เล็กและใหญ่) ที่มีชื่อไฟล์พื้นฐานคือ foo, bar หรือเลขทศนิยมตั้งแต่ 1 ถึง 20 ...

ksh93ตอนนี้ยังสามารถรวม regexps (พื้นฐาน, ขยาย, perl-like หรือ "augmented") ใน globs ของมัน (แม้ว่ามันจะค่อนข้าง buggy) และยังมีเครื่องมือในการแปลงระหว่าง glob และ regexp ( printf %R, printf %P):

echo ~(Ei:.*\.txt)

เพื่อจับคู่ (ไม่ซ่อน) ไฟล์ txt กับE xtended นิพจน์ปกติกรณีที่ฉันต้องการ


เยี่ยมบทความ! จริงๆคุณไม่สามารถใช้~(opt:pat)สำหรับตัวเลือกใด ๆ ที่เป็นตัวพิมพ์ใหญ่ print -r -- ~(Ei).*\.txt$บางที การวางรูปแบบด้านในนั้นดูเหมือนจะเป็นประโยชน์เฉพาะเพื่อหลีกเลี่ยงการสลับตัวเลือกแล้วปิดเพื่อเป็นส่วนหนึ่งของรูปแบบ แปลกคุณสามารถผสมและจับคู่หลายภาษารูปแบบภายในกลมเดียวกันแม้ว่า ~(Ki)*.~(E)txt$เทียบเท่า (ในที่สุดทุกอย่างเพิ่งถูกแปลงเป็น regex และส่งผ่านไปยังโปรแกรม regex ของ libast ภายใน)
ormaaj

@ormaaj ~(Ei:.*\.txt)ใช้ได้กับฉันด้วยอายุ 15 ปีเช่น ksh93 o +
Stéphane Chazelas

ทำงานร่วมกับหนึ่งในไบนารีทดสอบที่บันทึกไว้ของฉันด้วย (2014-12-24) แต่ฉันจำได้ว่าพบปัญหาเกี่ยวกับเรื่องนั้น สิ่งต่าง ๆ จะถูกสุ่มแตกหักและแก้ไขอีกครั้งระหว่างแต่ละเวอร์ชันเมื่อ ksh ยังคงพัฒนาในเชิงพาณิชย์ ฉันจำรหัสการจับคู่รูปแบบเป็นหนึ่งในพื้นที่ที่เปราะบาง
ormaaj

@ormaaj สิ่งหนึ่งที่แตกต่างกันระหว่าง~(E)xและ~(E:x)เป็นที่หลังยึด (ตรงกับxเฉพาะในขณะที่การแข่งขันเดิมในสิ่งที่มีx) ซึ่งอาจเป็นปัญหาที่คุณพบ (ใช้~(-lr)~(E:x)เพื่อลบการยึด~(E-lr:x)จะไม่ทำ) ไม่ว่าในกรณีใดฉันเห็นด้วยว่ามันค่อนข้างบั๊กแม้ในเวอร์ชันล่าสุด
Stéphane Chazelas

9

ภาษาปกติได้รับการแนะนำโดยKleeneในปี 1956 เอกสารน้ำเชื้อไม่ได้มีเครื่องหมายทันสมัยเต็มรูปแบบสำหรับการแสดงออกปกติ แต่มันก็แนะนำ "ดาว Kleen": A*ความหมาย "ซ้ำจำนวนใด ๆ ของA" ในทศวรรษหน้าสัญกรณ์มาตรฐานจะมีมากขึ้นหรือน้อยลงโดยเฉพาะอย่างยิ่ง.สำหรับตัวละครโดยพลการและ?หมายความว่าอักขระก่อนหน้านี้เป็นทางเลือก

สัญกรณ์ที่เปล่งประกายของ Bash เกิดจากglobคำสั่งนำมาตลอดทางในUnix v1ในปี 1971 ในขณะนั้นการทำ globbing ถูกดำเนินการโดยโปรแกรมแยกต่างหาก ต่อมามันถูกย้ายเข้าไปในเปลือก globคำสั่งเริ่มต้น?จะต้องหมายถึง "ตัวละครตัวใดตัวหนึ่ง" และ*หมายถึง "ลำดับของตัวละครใด ๆ " ฉันไม่รู้ว่าทำไมถึงเลือกตัวละคร; ?ค่อนข้างง่ายและ*อาจได้รับแรงบันดาลใจจากนิพจน์ปกติ

การปัดเศษไม่ได้ตั้งใจจะให้เป็นแบบทั่วไปเหมือนกับการแสดงออกปกติและการแสดงออกปกติยังไม่แพร่หลายมากในเวลานั้นดังนั้นจึงไม่มีการเรียกให้รวมแนวคิด จากจุดเริ่มต้นมีกันไม่ได้ประโยคด้วย?, .และ*มีความหมายที่แตกต่างกันในรูปแบบที่ชื่อไฟล์และในการแสดงออกปกติ

กระสุนสมัยใหม่เช่นทุบตีขยายตัวในรูปแบบ glob แต่มันเป็นวิวัฒนาการที่ค่อยๆรักษาความเข้ากันได้ย้อนหลัง Ksh88 (1988 รุ่นของเปลือกกร ) แนะนำไวยากรณ์ที่เพิ่มขึ้นสำหรับรูปแบบเปลือกซึ่งไม่อาจจะไวยากรณ์เช่นเดียวกับการแสดงออกปกติปกติ แต่เป็นแรงบันดาลใจอย่างมากโดยมัน*(PATTERN)จะหมายถึงจำนวนของการเกิดซ้ำของใด ๆPATTERN, @(PATTERN1|PATTERN2)หมายถึง“ PATTERN1หรือPATTERN2” เป็นต้น

bash รุ่นใหม่ (ตั้งแต่ 2.02) รองรับรูปแบบการขยายของ ksh88 หากคุณออกshopt -s extglobก่อน


Bash ไม่รองรับ extglobs หรือไม่? เท่าที่ฉันทราบ Bash, zsh และ {pd, m} ksh ได้สนับสนุน globs แบบเดียวกับที่บันทึกไว้ในคู่มือ ksh88 ตั้งแต่วันแรก ๆ Ksh มาจนถึงทุกวันนี้ไม่มีแม้แต่ตัวเลือกในการปิดการใช้งานตัวขยาย glob "แบบขยาย" และ ksh93 เป็นกลุ่มเดียวที่มีส่วนขยายใด ๆ นอกเหนือจากที่ ksh88 มี
ormaaj

2
@ormaaj Ksh88 ขยายความโกลาหลและextglobมีการแนะนำตัวเลือกใน bash 2.02 ที่ใดที่หนึ่งในรอบปี 1998 Zsh ได้รับมาksh_globในซีรีส์ 3.1 ที่ไหนสักแห่งในเวลาเดียวกัน Zsh มีส่วนขยายที่กลมกลืนมากมาย (บางตัวต้องใช้extended_globตัวเลือก)
Gilles 'ดังนั้นหยุดความชั่วร้าย'

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

1
@ormaaj โปรดทราบว่าในbashทางตรงกันข้ามkshextglob ทำให้ bash ไม่สอดคล้องกับ POSIX เนื่องจากไม่ได้ปิดการใช้งานในตัวแปร ในksh, var='@(*)'; echo $varขยายทุกชื่อไฟล์ใน dir ในปัจจุบันที่เริ่มต้นด้วย@(และสิ้นสุดใน)เป็น POSIX ต้องในขณะbash -O extglobที่มันขยายไปยังทุกไฟล์ (ยังคงมีอยู่หนึ่งอาจพิจารณาพฤติกรรมทุบตีทำให้รู้สึกที่นี่ (และพฤติกรรม ksh ค่อนข้างเจ็บปวดเมื่อคุณต้องการมีรูปแบบในตัวแปร)) ไวยากรณ์ glob นั้นน่าอึดอัดใจมากเนื่องจากความเข้ากันได้ของ POSIX / Bourne เปรียบเทียบกับ zsh ขยาย globs
Stéphane Chazelas

@ StéphaneChazelasนั่นเป็นเรื่องจริงทั้งหมดและฉันชอบวิธี ksh ที่ค่อนข้างฉลาดเกี่ยวกับเรื่องนี้ มันไม่ค่อยเข้ามาเล่นแม้ว่าจะมีข้อ จำกัด จริง ๆ กับ POSIX ด้วยเกือบทุกการใช้งานสำหรับ wordplitting แทนที่ด้วยคุณสมบัติที่ดีกว่าและการจัดเก็บรูปแบบในตัวแปรเป็นสิ่งที่สร้างความรำคาญอย่างมากเนื่องจากคุณต้องล้าง IFS ให้ว่างปิดการใช้งานการขยายรั้งทุกหนทุกแห่ง แต่ทุบตี ฉันคิดว่ามันคงเป็นไปไม่ได้ที่จะปลอดภัยอย่างสมบูรณ์กับรูปแบบการจัดเก็บ ปัญหาการหลีกเลี่ยงแบบเก่านี้ไม่เคยได้รับการแก้ไขอย่างแท้จริง
ormaaj

1

เหตุผลทางประวัติศาสตร์: ใช่ การอ้างอิง:
http://en.wikipedia.org/wiki/Glob_(programming)#Origin

เพียงแสดงความแตกต่างนี่เป็นตัวอย่างที่ดีและง่าย: a*

  • shell globbing: ความหมายคือตัวละครตัวแรกคือaอะไรก็ตาม (a, ab, abca ... )
  • regex: ความหมายคือการทำซ้ำอักขระศูนย์หรือมากกว่านั้นa(a, aa, aaa ... )

ฉันพร้อมยอมรับว่าความแตกต่างในความหมายนี้สร้างความสับสนอย่างมากสำหรับผู้ใช้ใหม่

Globbing อาจจะเข้าใจง่ายกว่าสำหรับผู้มาใหม่ แต่ก็มีโครงสร้างที่ทรงพลังน้อยกว่า

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