Làm thế nào để một vỏ (bash, ví dụ) mở rộng các mẫu ký tự đại diện?


9

Giả sử rằng một thư mục có 100 tệp bắt đầu bằng chữ 'a'.

Nếu tôi làm một grep <some string> a*từ thiết bị đầu cuối, vỏ sẽ xử lý việc này như thế nào?

Nó sẽ mở rộng biểu thức chính quy, có được danh sách tất cả các tệp bắt đầu bằng a và grep trên mỗi một trong số đó không? Hoặc có một số cách khác?

Giả sử rằng tôi có một loạt các tên tệp ở trên bắt đầu bằng 'a'. Sẽ mất nhiều thời gian hơn / ít hơn nếu tôi viết một vòng lặp for và tự lặp đi lặp lại trong một kịch bản shell hoặc chương trình ac?


7
BTW, đó globkhông phải là một biểu thức thông thường. Sự khác biệt lớn.
Aaron D. Marasco

Câu trả lời:


8

Đầu tiên, một nitpick: một chuỗi như a*trong cú pháp shell thông thường là một hình cầu, hoạt động khác với các biểu thức thông thường.

Trên một tổng quan cấp cao, trình thông dịch shell (tức là bash) mở rộng chuỗi a*thành một danh sách của mỗi tên tệp khớp với mẫu a*. Những phần sau đó trở thành các tham số dòng lệnh để một đơn thể hiện của grep(đối với các lập trình viên, tất cả các từ mở rộng đi như chuỗi riêng biệt vào các argvđối số của main). grepLệnh đơn đó sau đó phân tích các đối số theo bất kỳ cách nào nó chọn và tùy thuộc vào grepviệc diễn giải các đối số đó như tên tệp, tùy chọn, đối số tùy chọn, biểu thức chính quy, v.v. và thực hiện các hành động thích hợp. Mọi thứ xảy ra tuần tự (AFAIK không có greptriển khai sử dụng nhiều luồng).

Nếu bạn thực hiện một vòng lặp trong một tập lệnh shell để làm điều tương tự, nó gần như được đảm bảo là chậm hơn quy trình trên, vì những lý do sau. Nếu bạn sinh ra một quy trình grep mới cho mỗi tệp, nó chắc chắn sẽ chậm hơn do chi phí tạo quá trình được nhân lên một cách không cần thiết. Nếu bạn tự xây dựng danh sách đối số trong tập lệnh shell và sử dụng một thể hiện duy nhất grep, mọi thứ bạn làm trong shell sẽ vẫn chậm hơn vì các lệnh shell phải diễn giải (bằng bash), thêm một lớp mã bổ sung, và bạn sẽ chỉ cần thực hiện lại những gì bash đã thực hiện nhanh hơn trong mã được biên dịch.

Đối với việc tự viết bằng C, bạn có thể dễ dàng đạt được hiệu suất tương đương với quy trình được mô tả trong đoạn đầu tiên nhưng không chắc là bạn sẽ có thể đạt được mức tăng hiệu suất so với việc triển khai grep / bash hiện tại để chứng minh thời gian chi tiêu mà không đi sâu vào tối ưu hóa hiệu suất cụ thể của máy hoặc hy sinh tính di động. Có thể bạn có thể thử đưa ra một phiên bản song song tùy ý grep, nhưng thậm chí điều đó có thể không giúp ích gì vì bạn có nhiều khả năng bị ràng buộc I / O hơn giới hạn CPU. Mở rộng Glob và grep đã "đủ nhanh" cho hầu hết các mục đích "bình thường".


Cảm ơn câu trả lời rất chi tiết. Trên thực tế, tôi cần grep gzip tệp (mỗi GB vài GB). Tôi có một danh sách các tập tin đó. Bây giờ tôi có một lựa chọn hoặc là xây dựng một regex (phức tạp) để khớp các tệp đó hoặc lặp qua danh sách đã biết và chạy grep trên mỗi một trong số chúng (dễ dàng). Do đó lo lắng về hiệu suất.
harithski

cố gắng zcatzgrep; không cần giải nén từng cái một
jw013

Phải, tất nhiên. Tôi đang sử dụng zgrep.
harithski

6

Có, nó sẽ mở rộng thành một danh sách các tệp và cung cấp danh sách kết quả cho grepchương trình. Ít nhất đó là những gì man bashnói trong phần mở rộng Pathname mở rộng .

Có một cách khác để sử dụng mở rộng trong các trường hợp đơn giản như bạn đề cập: viết grep <some_string> atrước khi nhấn* , nhấn ESC. Điều này sẽ mở rộng danh sách các tệp phù hợp ngay trong dòng lệnh, vì vậy bạn có thể xác minh danh sách là OK trước khi nhấn Enter.

Đối với phần thứ hai của câu hỏi của bạn, nó phụ thuộc. Nếu bạn muốn viết một vòng lặp for chạy lần lượt grep trên mỗi tệp, thì nó chắc chắn sẽ chậm hơn, bởi vì chương trình grep sẽ được chạy không chỉ một lần, mà là một lần cho mỗi tệp. Tuy nhiên, những gì quan trọng cần lưu ý là có một số giới hạn về độ dài mở rộng của các đối số dòng lệnh, bạn có thể sử dụng, mặc dù nó thường là khá cao. Để thấy điều đó, bạn có thể thử grep adasdsadf /usr/*/*/* >/dev/null.


2
ESC+*không hoàn toàn giống như để bash mở rộng * bởi vì ESC+*sẽ chèn dotfiles (tên bắt đầu bằng a .) trong khi việc mở rộng *phụ thuộc vào dotglob shoptcài đặt. Trình tự chính để mở rộng và chèn các khối là C-x *theo mặc định và ánh xạ tới lệnh readline glob-expand-word.
jw013

1
@ jw013 Cảm ơn thông tin! Nó dường như không thay đổi trường hợp a*mở rộng, nhưng chắc chắn là quan trọng trong phạm vi rộng hơn.
rozcietrzewiacz

2
zshlưu ý: chỉ cần nhấn phím tab trên các tham số có thể mở rộng (mô hình toàn cầu, mở rộng dấu ngoặc, thay thế lệnh, xóa) sẽ mở rộng chúng.
Stéphane Gimenez

@ jw013 Trên thực tế, tôi chỉ thử nghiệm C-xlối tắt và nó không mở rộng danh sách các tệp trên hệ thống của tôi (sử dụng bash).
rozcietrzewiacz

1
@roz Phải - Tôi hầu như không bao giờ sử dụng nó dù sao, chỉ muốn chỉ ra sự khác biệt (khá nitpicky) :). C-x *chỉ có những quả cầu mà chỉ làm tên tập tin, nhưng Esc *thực sự còn làm được nhiều hơn thế insert-completions, vì trong tất cả các lần hoàn thành có thể. Điều này có nghĩa là sử dụng Esc *trên một dòng lệnh trống sẽ chèn tên của mỗi tệp thực thi duy nhất trong $PATHví dụ của bạn .
jw013
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.