Lịch sử của Bash thế giới


11

Có một lý do lịch sử tại sao Bash "continbing" và các biểu thức chính quy không giống nhau không? Ví dụ, tôi tin rằng trong Bash [1-2]*khớp với bất kỳ thứ gì bắt đầu bằng 1 hoặc 2 theo sau bởi bất kỳ thứ gì khác, trong khi như một biểu thức thông thường [1-2]*sẽ chỉ khớp với một chuỗi 1 và 2 giây. Kịch bản Bash của tôi và REGEX foo đều khá yếu và tôi thường xuyên gặp phải các vấn đề liên quan đến những khác biệt này khiến tôi tò mò là tại sao chúng lại khác nhau.


3
Bạn sẽ xem xét làm rm -- ^[^.].*\.txt$thay vì rm -- *.txt?
Stéphane Chazelas

1
Phần lớn Q của bạn được chạm vào trong chủ đề này từ lwn: lwn.net/Articles/96687
slm

Có các lệnh hoạt động trên tên tệp và lấy regrec. Ví dụ: tìm, find . -regex ".*\.txt$" | xargs rm --hoặc renameđể đổi tên các tệp (nó seddành cho tên tệp), hãy cẩn thận một số hệ thống có khác rename.
ctrl-alt-delor

@richard, tôi ^[^.].*\.txt$đã tính đến việc bỏ qua các tập tin dấu chấm. Lưu ý rằng đó -regexlà một phần mở rộng GNU, một số shell như ksh93 hoặc zsh có thể kết hợp regexps trong các khối của chúng (ví dụ: thử ksh93 -c 'echo ~(E:^[^.].*\.txt$)':)
Stéphane Chazelas

2
Bash đó tuân theo thực tiễn hiện có một cách cẩn thận trong khi tránh các thay đổi và phần mở rộng không tương thích không thể hòa giải là một trong những thế mạnh lớn nhất của nó.
ormaaj

Câu trả lời:


12

bashban đầu được thiết kế vào cuối những năm 80 như là một bản sao một phần kshvới một số tính năng tương tác từ csh / tcsh.

Nguồn gốc của Globing phải được tìm thấy trong những chiếc vỏ trước đó mà nó tạo ra.

kshchính nó là một phần mở rộng của vỏ Bourne. Bản thân vỏ Bourne (phát hành lần đầu tiên vào năm 1979 trong Unix V7) là một bản triển khai rõ ràng từ đầu, nhưng nó không hoàn toàn rời khỏi vỏ Thompson (vỏ của V1 -> V6) và các tính năng kết hợp từ vỏ Mashey.

Cụ thể, các đối số lệnh vẫn được phân tách bằng khoảng trắng, |giờ là toán tử đường ống mới nhưng ^vẫn được hỗ trợ như một giải pháp thay thế (và cũng giải thích lý do tại sao bạn làm [!a-z]và không [^a-z]), $1vẫn là đối số đầu tiên cho tập lệnh và dấu gạch chéo ngược vẫn là ký tự thoát . Vì vậy, nhiều toán tử regrec ( ^\|$) có ý nghĩa đặc biệt của riêng chúng trong shell.

Vỏ của Thompson dựa vào một tiện ích bên ngoài để đánh bóng. Khi shtìm thấy không được trích dẫn *, [hoặc ?s trong lệnh, nó sẽ chạy lệnh thông qua glob.

rm *.txt

cuối cùng sẽ chạy toàn cầu như:

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

và global sẽ kết thúc rmvới danh sách các tệp khớp với mẫu đó.

grep a.\*b *.txt

sẽ chạy globnhư:

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

*trên đã được trích dẫn bằng cách đặt bit thứ 8 cho ký tự đó, ngăn globkhông cho nó là ký tự đại diện. globsau đó sẽ loại bỏ bit đó trước khi gọi grep.

Để làm tương đương với regexps, đó sẽ là:

regexp rm '\.txt$'

Hoặc là:

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

để loại trừ các tập tin dấu chấm.

Sự cần thiết phải thoát khỏi các toán tử khi chúng nhân đôi các ký tự đặc biệt, thực tế là ., phổ biến trong tên tệp là một toán tử regrec làm cho nó không phù hợp lắm với các tên tệp và phức tạp cho người mới bắt đầu. Trong hầu hết các trường hợp, tất cả những gì bạn cần là các ký tự đại diện có thể thay thế một ( ?) hoặc bất kỳ số ( *) ký tự nào.

Bây giờ, các shell khác nhau đã thêm các toán tử Globing khác nhau. Ngày nay, các khối ksh và zsh (và ở một mức độ nào bash -O extglobđó thực hiện một tập hợp con của các khối ksh) có chức năng tương đương với các biểu thức chính quy với một cú pháp ít cồng kềnh hơn để sử dụng với tên tệp và cú pháp shell hiện tại. Chẳng hạn, trong zsh(với phần mở rộng được mở rộng), bạn có thể làm:

echo a#.txt

nếu bạn muốn (không chắc) khớp với tên tệp bao gồm các chuỗi atheo sau .txt. Dễ dàng hơn echo (^a*\.txt$)(ở đây sử dụng dấu ngoặc nhọn như một cách để cô lập các toán tử regex khỏi các toán tử shell có thể là một cách mà shell có thể đối phó với nó).

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

Đối với các tệp mpg (không phân biệt chữ hoa chữ thường) có tên cơ sở là foo, bar hoặc số thập phân từ 1 đến 20 ...

ksh93bây giờ cũng có thể kết hợp regexps (cơ bản, mở rộng, giống như perl hoặc "tăng cường") trong các khối của nó (mặc dù nó khá lỗi) và thậm chí cung cấp một công cụ để chuyển đổi giữa global và regrec ( printf %R, printf %P):

echo ~(Ei:.*\.txt)

để khớp các tệp txt (không bị ẩn) với các biểu thức chính quy E xtends, case- i nsensitively.


Tuyệt vời viết lên! Bạn thực sự không thể sử dụng ~(opt:pat)cho bất kỳ tùy chọn viết hoa nào. Có lẽ print -r -- ~(Ei).*\.txt$. Đặt mẫu bên trong dường như chỉ hữu ích để tránh phải bật tùy chọn sau đó tắt cho một phần của mẫu. Điều kỳ lạ là bạn có thể trộn và kết hợp nhiều ngôn ngữ mẫu trong cùng một thế giới. ~(Ki)*.~(E)txt$là tương đương (Cuối cùng, mọi thứ chỉ được chuyển đổi thành regex và được chuyển sang công cụ regex của libast trong nội bộ).
ormaaj

@ormaaj, ~(Ei:.*\.txt)hoạt động với tôi ngay cả với các phiên bản 15 tuổi như ksh93 o +.
Stéphane Chazelas

Cũng hoạt động với một trong các tệp nhị phân kiểm tra đã lưu của tôi (2014-12-24), nhưng tôi nhớ lại việc gặp phải vấn đề với điều đó. Mọi thứ luôn bị phá vỡ ngẫu nhiên và được sửa lại giữa mỗi phiên bản khi ksh vẫn được phát triển thương mại. Tôi nhớ mã khớp mẫu là một trong những khu vực dễ vỡ.
ormaaj

@ormaaj, một khác nhau giữa ~(E)x~(E:x)là sau này được neo (trận đấu trên xchỉ trong khi các trận đấu trước đây về bất cứ điều gì chứa x), đây có thể là loại vấn đề bạn chạy vào (sử dụng ~(-lr)~(E:x)để loại bỏ các neo, ~(E-lr:x)sẽ không làm). Trong mọi trường hợp, tôi đồng ý rằng nó khá lỗi, ngay cả trong phiên bản mới nhất.
Stéphane Chazelas

9

Các ngôn ngữ thông thường được Kleene giới thiệu vào năm 1956. Bài báo bán nguyệt không có ký hiệu hiện đại đầy đủ cho các biểu thức chính quy, nhưng nó đã giới thiệu ngôi sao Kle Kleen star: A*có nghĩa là bất kỳ số lần lặp lại nào của Hồi A. Trong thập kỷ tiếp theo, một số ký hiệu tiêu chuẩn ít nhiều đã xuất hiện, đặc biệt là .một ký tự tùy ý và ?có nghĩa là ký tự trước đó là tùy chọn.

Ký hiệu toàn cầu của Bash bắt nguồn từ globlệnh được giới thiệu hoàn toàn trong Unix v1 năm 1971. Vào thời điểm đó, Globing được thực hiện bởi một chương trình riêng biệt; sau đó nó đã được chuyển vào vỏ. Đầu globlệnh có ?nghĩa là “bất kỳ một nhân vật” và *có nghĩa là “mọi chuỗi ký tự”. Tôi không biết tại sao các nhân vật được chọn; ?là khá trực quan, và *có thể đã được truyền cảm hứng từ một trong các biểu thức thông thường.

Globbing không có ý định chung chung như các biểu thức thông thường và các biểu thức chính quy không phổ biến vào thời điểm đó, vì vậy không có lời kêu gọi để thống nhất các khái niệm. Ngay từ đầu, đã có sự không tương thích cú pháp, với ?, .*có nghĩa là những thứ khác nhau trong mẫu tên tập tin và trong biểu thức thông thường.

Các lớp vỏ hiện đại như bash mở rộng trên các mẫu toàn cầu, nhưng đó là sự tiến hóa dần dần duy trì khả năng tương thích ngược. Ksh88 (phiên bản 1988 của shell Korn ) đã giới thiệu một cú pháp mở rộng cho các mẫu shell, không thể là cú pháp giống như các biểu thức thông thường thông thường nhưng được truyền cảm hứng mạnh mẽ bởi nó: *(PATTERN)có nghĩa là bất kỳ số lần lặp lại nào PATTERN, @(PATTERN1|PATTERN2)có nghĩa là PATTERN1hay hay PATTERN2, Vân vân.

Các phiên bản hiện đại của bash (kể từ 2.02) hỗ trợ các mẫu mở rộng của ksh88, nếu bạn phát hành shopt -s extglobtrước.


Có bao giờ Bash không được hỗ trợ extglobs? Theo như tôi biết Bash, zsh và {pd, m} ksh đã hỗ trợ các chính xác như các tài liệu trong tài liệu hướng dẫn ksh88 từ những ngày đầu. Ksh cho đến ngày nay thậm chí không có tùy chọn để vô hiệu hóa bộ lượng tử toàn cầu "mở rộng" và ksh93 là người duy nhất trong số các nhóm có bất kỳ tiện ích mở rộng nào ngoài những gì ksh88 có.
ormaaj

2
@ormaaj Ksh88 mở rộng các extglobtùy chọn và tùy chọn đã được giới thiệu trong bash 2.02 ở đâu đó vào khoảng năm 1998. Zsh có được ksh_globtrong loạt 3.1 ở đâu đó cùng một lúc. Zsh có nhiều phần mở rộng toàn cầu của riêng mình (một số yêu cầu extended_globtùy chọn).
Gilles 'SO- đừng trở nên xấu xa'

Tôi hiểu rồi. Vì vậy, nó thực sự đã đủ muộn để biện minh cho sự cần thiết cho một lựa chọn. (Tôi nghĩ mặc định tắt là khá vô nghĩa những ngày này nhưng, thật thú vị.)
ormaaj

1
@ormaaj, Lưu ý rằng bash, ngược lại ksh, extglob làm cho bash không tuân thủ POSIX vì nó không bị vô hiệu hóa trong các biến. Trong ksh, var='@(*)'; echo $varmở rộng cho tất cả các tên tệp trong thư mục hiện tại bắt đầu @(và kết thúc )như POSIX yêu cầu trong khi trong bash -O extglobđó mở rộng ra tất cả các tệp. (tuy nhiên, người ta có thể xem xét hành vi bash có ý nghĩa hơn ở đây (và hành vi ksh khá đau khi bạn muốn có các mẫu trong các biến)). Cú pháp toàn cầu đó rất khó xử vì điều đó (tương thích POSIX / Bourne). So sánh với zsh mở rộng.
Stéphane Chazelas

@ StéphaneChazelas Điều đó hoàn toàn đúng, và tôi thích cách ksh có phần thông minh về nó. Nó hiếm khi đến để chơi mặc dù trừ khi thực sự bị ràng buộc với POSIX. Với hầu hết mọi cách sử dụng để thay thế từ được thay thế bằng các tính năng tốt hơn và việc lưu trữ các mẫu trong các biến là một điều cực kỳ phiền toái vì bạn phải làm trống IFS, vô hiệu hóa mở rộng dấu ngoặc ở mọi nơi trừ bash. Tôi nghĩ rằng vẫn không thể hoàn toàn an toàn với các mẫu được lưu trữ. Vấn đề thoátnày không bao giờ thực sự được giải quyết chẳng hạn.
ormaaj

1

Lý do lịch sử: CÓ. Tham khảo:
http://en.wikipedia.org/wiki/Glob_(programming)#Origin

Chỉ để thể hiện sự khác biệt, đây là một ví dụ hay và dễ hiểu: a*

  • shell globalbing: có nghĩa là, ký tự đầu tiên avà sau đó là bất cứ điều gì (a, ab, abca ...)
  • regex: có nghĩa là, không hoặc nhiều lần lặp lại của ký tự a(a, aa, aaa ...)

Tôi sẵn sàng đồng ý rằng sự khác biệt về ý nghĩa này rất khó hiểu đối với người dùng mới.

Globbing có lẽ dễ nắm bắt hơn đối với người mới, nhưng nó cũng ít mạnh mẽ hơn.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.