Cách xử lý mỗi dòng nhận được do kết quả của lệnh grep


152

Tôi có một số dòng được lấy từ một tệp sau khi chạy lệnh grep như sau:

var=`grep xyz abc.txt`

Giả sử tôi có 10 dòng bao gồm xyz.

Bây giờ tôi cần xử lý từng dòng tôi nhận được do kết quả của lệnh grep. Làm thế nào để tôi tiến hành cho điều này?


6
Không có câu trả lời nào ở đây đề cập đến sức mạnh của grep -oloại điều này. Các -olá cờ sẽ trả lại chỉ văn bản mà các trận đấu, với một trận đấu trên mỗi dòng sản lượng. (Nó không đầy đủ, vì vậy echo aaa |grep 'a*'chỉ cung cấp cho bạn "aaa" và bỏ qua ba phần khớp "", "a" và "aa")
Adam Katz

Câu trả lời:


269

Một trong những cách dễ dàng là không lưu trữ đầu ra trong một biến, mà lặp lại trực tiếp trên nó với một vòng lặp while / read.

Cái gì đó như:

grep xyz abc.txt | while read -r line ; do
    echo "Processing $line"
    # your code goes here
done

Có các biến thể trong sơ đồ này tùy thuộc vào chính xác những gì bạn đang theo đuổi.

Nếu bạn cần thay đổi các biến trong vòng lặp (và có thể thấy sự thay đổi đó bên ngoài vòng lặp), bạn có thể sử dụng thay thế quy trình như được nêu trong câu trả lời của fedorqui's :

while read -r line ; do
    echo "Processing $line"
    # your code goes here
done < <(grep xyz abc.txt)

nếu không có dòng với xyz?
XYZ_Linux

Sau đó, không có gì xảy ra, vòng lặp không chạy.
Mat

13
Vấn đề với phương pháp này là (vì đường ống) mọi thứ bên trong vòng lặp đều nằm trong một khung con, do đó, việc đặt các biến được xác định bên ngoài vòng lặp trong vòng lặp không làm cho giá trị của chúng có sẵn sau vòng lặp!
David Doria

3
@David: cung cấp một giải pháp thay thế để giải quyết mối quan tâm của bạn. (fedorqui cũng đã giải quyết nó.)
Mat

Đối với các lệnh có dòng đầu ra cuối cùng không bị chấm dứt với một dòng mới, bạn cần: while read p || [[ -n $p ]]; do ...(mượn từ stackoverflow.com/questions/1521462/ trộm )
Ohad Schneider

21

Bạn có thể thực hiện while readvòng lặp sau , sẽ được cung cấp bởi kết quả của greplệnh bằng cách sử dụng cái gọi là quá trình thay thế:

while IFS= read -r result
do
    #whatever with value $result
done < <(grep "xyz" abc.txt)

Bằng cách này, bạn không phải lưu trữ kết quả trong một biến, nhưng trực tiếp "đưa" đầu ra của nó vào vòng lặp.


Lưu ý việc sử dụng IFS=read -rtheo các khuyến nghị trong BashFAQ / 001: Làm cách nào tôi có thể đọc từng tệp (luồng dữ liệu, biến) theo từng dòng (và / hoặc theo từng trường)? :

Tùy chọn -r để đọc ngăn chặn diễn giải dấu gạch chéo ngược (thường được sử dụng như một cặp dòng mới dấu gạch chéo ngược, để tiếp tục qua nhiều dòng hoặc để thoát các dấu phân cách). Nếu không có tùy chọn này, mọi dấu gạch chéo ngược không được giải quyết trong đầu vào sẽ bị loại bỏ. Bạn hầu như luôn luôn nên sử dụng tùy chọn -r với đọc.

Trong kịch bản trên IFS = ngăn chặn việc cắt xén khoảng trắng hàng đầu và dấu. Loại bỏ nó nếu bạn muốn hiệu ứng này.

Về việc thay thế quy trình, nó được giải thích trong trang tin tặc bash :

Thay thế quy trình là một hình thức chuyển hướng trong đó đầu vào hoặc đầu ra của một quá trình (một số chuỗi lệnh) xuất hiện dưới dạng một tệp tạm thời.


OK, tôi tấn công forphiên bản. Đã thử thực hiện một vòng lặp "${$(grep xyz abc.txt)[@]}"như trong stackoverflow.com/a/14588210/1983854 nhưng không thể. Vì vậy, tôi chỉ để lại phiên bản đầu tiên.
fedorqui 'SO ngừng làm hại'

1
Bạn không thể áp dụng mở rộng tham số cho việc thay thế lệnh (trừ khi bạn đang sử dụng zsh, nơi kiểu lồng đó có thể hoạt động).
chepner

Một vấn đề có thể xảy ra với thành ngữ này là nếu bất cứ điều gì trong vòng lặp cố gắng đọc từ đầu vào tiêu chuẩn, nó sẽ nhận được một phần của tệp. Để tránh khả năng này, tôi muốn gửi tệp thông qua mô tả tệp 3 chứ không phải stdin. Chỉ cần sử dụng while IFS= read -r result <&3done 3< <(grep ...
Gordon Davisson

8

Tôi sẽ đề nghị sử dụng awk thay vì grep + một cái gì đó khác ở đây.

awk '$0~/xyz/{ //your code goes here}' abc.txt


1
Làm thế nào bạn sẽ tham khảo các dòng tìm thấy đầy đủ trong //your code goes here?
dùng857990

2
@ user857990 Toàn bộ dòng được biểu thị là $ 0 trong AWK.
Julien Grenier

2

Không có bất kỳ sự lặp lại nào với tùy chọn grep được đệm dòng:

your_command | grep --line-buffered "your search"

Ví dụ thực tế với một lệnh gỡ lỗi của bộ định tuyến Symfony PHP Framework, để grep tất cả các tuyến liên quan đến "api":

php bin/console d:r | grep --line-buffered "api"

Giải pháp duy nhất hoạt động nếu your_command hoạt động lâu dài (chẳng hạn như tail -f some.log, trong trường hợp của tôi) ...
Izkata

0

Thường thì thứ tự xử lý không quan trọng. GNU Parallel được tạo cho tình huống này:

grep xyz abc.txt | parallel echo do stuff to {}

Nếu bạn xử lý giống như:

grep xyz abc.txt | myprogram_reading_from_stdin

myprogramchậm thì bạn có thể chạy:

grep xyz abc.txt | parallel --pipe myprogram_reading_from_stdin

0

Lặp lại kết quả grep với vòng lặp while / read. Giống:

grep pattern filename.txt | while read -r line ; do
    echo "Matched Line:  $line"
    # your code goes here
done

0

Đối với những người tìm kiếm một lót:

grep xyz abc.txt | while read -r line; do echo "Processing $line"; done
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.