tập tin grep từ danh sách


14

Tôi đang cố gắng chạy grep với danh sách vài trăm tệp:

$ head -n 3 <(cat files.txt)
admin.php
ajax/accept.php
ajax/add_note.php

Tuy nhiên, mặc dù tôi đang tìm kiếm một chuỗi mà tôi biết được tìm thấy trong các tệp, nhưng sau đây không tìm kiếm các tệp:

$ grep -i 'foo' <(cat files.txt)

$ grep -i 'foo' admin.php
The foo was found

Tôi quen thuộc với -fcờ sẽ đọc các mẫu từ một tệp. Nhưng làm thế nào để đọc các tập tin đầu vào ?

Tôi đã xem xét cách giải quyết khủng khiếp của việc sao chép các tệp vào một thư mục tạm thời cpdường như hỗ trợ <(cat files.txt)định dạng và từ đó lấy các tệp. Shirley có một cách tốt hơn.

Câu trả lời:


22

Bạn dường như đang gặt danh sách tên tập tin, không phải chính các tập tin. <(cat files.txt)chỉ liệt kê các tập tin Cố gắng <(cat $(cat files.txt))thực sự nối chúng và tìm kiếm chúng dưới dạng một luồng hoặc

grep -i 'foo' $(cat files.txt)

để cung cấp cho grep tất cả các tập tin.

Tuy nhiên, nếu có quá nhiều tệp trong danh sách, bạn có thể gặp vấn đề với số lượng đối số. Trong trường hợp đó tôi chỉ cần viết

while read filename; do grep -Hi 'foo' "$filename"; done < files.txt

Cảm ơn bạn! Tôi đã không nhận ra rằng whilecó thể nhận được các dòng file.txt như vậy.
dotancohen

Bạn sẽ muốn vô hiệu hóa phần toàn cầu của toán tử split + global đó tại đây (trừ khi shell là zsh).
Stéphane Chazelas

1
whilekhông chính xác nhận được các dòng từ tệp, readđang làm điều đó; whilechỉ cho phép chúng tôi làm điều đó trong một vòng lặp. Vòng lặp kết thúc khi readthất bại (tức là trả về mã trả về khác không), thông thường là do Đạt đến tệp kết thúc.
PM 2Ring

1
Để đọc một dòng (văn bản), cú pháp là IFS= read -r filename, read filenamemột cái gì đó khác.
Stéphane Chazelas

1
Lưu ý rằng đó -Hlà một phần mở rộng GNU. Bạn đang thiếu một số --.
Stéphane Chazelas

8
xargs grep -i -- foo /dev/null < files.txt

giả sử các tệp là trống hoặc dòng mới được phân tách (trong đó dấu ngoặc kép hoặc dấu gạch chéo ngược có thể được sử dụng để thoát khỏi các dấu phân cách đó). Với GNU, xargsbạn có thể chỉ định dấu phân cách với -d(sau đó vô hiệu hóa xử lý trích dẫn).

(unset -v IFS; set -f; grep -i -- foo $(cat files.txt))

giả sử các tệp là không gian, tab hoặc dòng mới được phân tách (không có cách nào thoát khỏi chúng mặc dù bạn có thể chọn một dấu tách khác bằng cách gán nó cho IFS). Điều đó sẽ thất bại nếu danh sách tệp quá lớn trên hầu hết các hệ thống.

Những người cũng cho rằng không có tệp nào được gọi -.


Nó là tốt hơn / nhanh hơn để sử dụng $(< file)thay vì $(cat file), ít nhất là trong bashzsh.
jimmij

7

Để đọc danh sách tên tập tin từ stdin bạn có thể sử dụng xargs. Ví dụ,

cat files.txt | xargs -d'\n' grep -i -- 'foo'

Theo mặc định, xargsđọc các mục từ đầu vào tiêu chuẩn, được phân cách bằng khoảng trắng. Nó -d'\n'bảo nó sử dụng dòng mới làm dấu phân cách đối số, vì vậy nó có thể xử lý tên tệp chứa khoảng trống. (Như Stéphane Chazelas chỉ ra, đó là một phần mở rộng GNU). Tuy nhiên, nó sẽ không đối phó với tên tệp chứa dòng mới; chúng ta cần một cách tiếp cận phức tạp hơn một chút để xử lý chúng.

FWIW, cách tiếp cận này có phần nhanh hơn một while readvòng lặp, vì readlệnh của bash rất chậm - nó đọc ký tự dữ liệu của nó theo ký tự, trong khi xargsđọc đầu vào của nó hiệu quả hơn. Ngoài ra, xargschỉ gọi greplệnh nhiều lần, với mỗi lệnh gọi nhận được nhiều tên tệp và điều đó hiệu quả hơn so với việc gọi grepriêng cho từng tên tệp.

Xem trang man xargs và trang thông tin xargs để biết thêm chi tiết.


3

xargscó thể đọc các mục từ một tệp (như files.txtdanh sách của bạn ) với tùy chọn của nó:

   --arg-file=file
   -a file
          Read items from file instead of standard input.  If you use this
          option, stdin remains unchanged when commands are  run.   Other
          wise, stdin is redirected from /dev/null.

Vì vậy, điều này cũng nên làm việc:

xargs -a files.txt grep -i 'foo'

hoặc cho khoảng trắng trong tên tệp

xargs -d'\n' -a files.txt grep -i 'foo'
xargs -I{} -a files.txt grep -i 'foo' {}

1

Bạn cũng có thể làm một ví dụ nhưng ví dụ của Orion là đơn giản nhất:

for i in $(cat files.txt); do grep -i 'foo' $i ; done

(Đối với mỗi tệp được liệt kê trong files.txt, hãy thực hiện lệnh grep trên 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.