2014-10-14 12 views
5

Trong một kịch bản bash, tôi có một danh sách các dòng trong một tập tin tôi muốn grep và sau đó hiển thị trên tiêu chuẩn ra, đó là dễ dàng nhất thực hiện với một thời gian đọc:grep, thông điệp in khác không có trận đấu

grep "regex" "filepath" | while read line; do 
    printf "$line\n" 
done 

Tuy nhiên, tôi muốn thông báo cho người dùng nếu không có dòng nào được khớp với grep. Tôi biết rằng người ta có thể làm điều này bằng cách updating a variable inside the loop nhưng có vẻ như một cách tiếp cận thanh lịch hơn nhiều (nếu có thể) sẽ cố gắng đọc một dòng trong vòng lặp cho đến khi không có đầu ra, một thông báo lỗi có thể được hiển thị.

Đây là nỗ lực đầu tiên của tôi:

grep "regex" "filepath" | until [[ -z ${read line} ]]; do 
    if [[ -z $input ]]; then 
     printf "No matches found\n" 
     break 
    fi 
    printf "$line\n" 
done 

Nhưng trong trường hợp này các lệnh đọc bị thay đổi, và tôi đã không chắc chắn về một cách khác các cụm từ truy vấn. Cách tiếp cận này có thể, và nếu không, liệu có một giải pháp phù hợp hơn cho vấn đề này?

+2

Trong ví dụ đầu tiên của bạn, chỉ cần có grep và bạn sẽ có được hành vi tương tự. Đây có phải chỉ là một ví dụ hay là những gì bạn thực sự muốn làm? – jordanm

+0

Có phải không? Bạn có thể chứng minh như thế nào? Tôi vẫn còn khá mới để bash và không hiểu làm thế nào để sử dụng grep để có được hành vi đó. Mã tôi đã viết cho thấy kết quả cuối cùng tôi đang cố gắng đạt được, để trả lời câu hỏi của bạn. Ah mèo là thừa. Đã xóa, cảm ơn bạn. – seraththundhuil

+0

grep in các dòng phù hợp với đầu ra tiêu chuẩn của nó theo mặc định, đó là lý do tại sao vòng lặp while/read hoạt động, nó đang đọc đầu vào tiêu chuẩn. Chỉ cần chạy 'grep 'regex" "filepath" 'và bạn sẽ thấy các dòng khớp của bạn. –

Trả lời

13

Bạn không cần lặp lại nếu bạn chỉ muốn hiển thị thông báo khi không khớp. Thay vào đó bạn có thể sử dụng mã trả về của grep. Một tuyên bố đơn giản if sẽ đủ:

if ! grep "regex" "filepath"; then 
    echo "no match" >&2 
fi 

này sẽ hiển thị các kết quả của trận đấu grep (vì đó là hành vi mặc định grep), và sẽ hiển thị thông báo lỗi nếu nó không.

Cách thay thế phổ biến cho if ! là sử dụng toán tử ||. foo || bar có thể được đọc là "làm foo hoặc khác làm bar" hoặc "nếu không foo thì bar".

grep "regex" "filepath" || echo "no match" >&2 
1

Như đã đề cập bởi @jordanm, không cần lặp lại trong trường hợp sử dụng mà bạn đã đề cập.

output=$(grep "regex" "file") 
if [[ -n $output ]]; then 
    echo "$output" 
else 
    echo "Sorry, no results..." 
fi 

Nếu bạn cần để lặp qua các kết quả cho chế biến (thay vì chỉ hiển thị để stdout) sau đó bạn có thể làm một cái gì đó như thế này:

output=$(grep "regex" "file") 
if [[ -n $output ]]; then 
    while IFS= read -r line; do 
     # do something with $line 
    done <<< "$output" 
else 
    echo "Sorry, no results..." 
fi 

Phương pháp này tránh sử dụng một đường ống hoặc subshell để bất kỳ bài tập biến nào được thực hiện trong vòng lặp sẽ có sẵn cho phần còn lại của tập lệnh.

Ngoài ra, tôi không chắc chắn nếu điều này liên quan đến những gì bạn đang cố gắng làm gì cả, nhưng grep không có khả năng tải mẫu từ một tệp (một tệp trên mỗi dòng). Nó được gọi như sau:

grep search_target -f pattern_file.txt 
1

Câu trả lời của John Kugelman là câu trả lời chính xác và ngắn gọn và bạn nên chấp nhận câu trả lời đó. Tôi đang giải quyết câu hỏi của bạn về cú pháp ở đây chỉ để hoàn thành.

Bạn không thể sử dụng ${read line} để thực thi read - cú pháp cú đúp thực sự có nghĩa là (mơ hồ) mà bạn muốn giá trị của biến có tên chứa khoảng trắng. Có lẽ bạn đã chụp cho $(read line) nhưng thực sự, những cách thích hợp để viết until vòng lặp của bạn sẽ được nhiều hơn dọc theo dòng của

grep "regex" "filepath" | until read line; [[ -z "$line" ]]; do 

... nhưng tất nhiên, khi không có đầu ra, đường ống sẽ không nhận được dòng , do đó, whileuntil đều sai ở đây.

Thật đáng giá vì lý do bạn cần một do riêng biệt là bạn có thể có nhiều lệnh trong đó. Ngay cả một cái gì đó giống như

while output=$(grep "regex filepath"); echo "grep done, please wait ..."; 
    count=$(echo "$output" | wc -l); [[ $count -gt 0 ]] 
do ... 

mặc dù một lần nữa, đó là nhiều phức tạp hơn bạn thực sự cần. (Và trong trường hợp cụ thể này, bạn có thể muốn thực sự muốn if, không phải while.)

Như những người khác đã lưu ý, không có lý do gì để sử dụng vòng lặp giống như ở đây, nhưng tôi muốn phân loại câu hỏi về cách để viết một vòng lặp như thế này cho bất cứ khi nào bạn thực sự muốn một.

Các vấn đề liên quan