grep cho nhiều chuỗi trong tệp trên các dòng khác nhau (tức là toàn bộ tệp, không phải tìm kiếm dựa trên dòng)?


85

Tôi muốn grep cho các tập tin có chứa các từ Dansk, Svenskahoặc Norsktrên bất kỳ dòng, với một mã trả có thể sử dụng (như tôi thực sự chỉ muốn có những thông tin rằng chuỗi được chứa, tôi một lót đi xa hơn một chút thì đây).

Tôi có nhiều tệp với các dòng như thế này:

Disc Title: unknown
Title: 01, Length: 01:33:37.000 Chapters: 33, Cells: 31, Audio streams: 04, Subpictures: 20
        Subtitle: 01, Language: ar - Arabic, Content: Undefined, Stream id: 0x20, 
        Subtitle: 02, Language: bg - Bulgarian, Content: Undefined, Stream id: 0x21, 
        Subtitle: 03, Language: cs - Czech, Content: Undefined, Stream id: 0x22, 
        Subtitle: 04, Language: da - Dansk, Content: Undefined, Stream id: 0x23, 
        Subtitle: 05, Language: de - Deutsch, Content: Undefined, Stream id: 0x24, 
(...)

Đây là mã giả của những gì tôi muốn:

for all files in directory;
 if file contains "Dansk" AND "Norsk" AND "Svenska" then
 then echo the filename
end

Cách tốt nhất để làm việc này là gì? Nó có thể được thực hiện trên một dòng không?

Câu trả lời:


89

Bạn có thể dùng:

grep -l Dansk * | xargs grep -l Norsk | xargs grep -l Svenska

Nếu bạn cũng muốn tìm trong các tệp ẩn:

grep -l Dansk .* | xargs grep -l Norsk | xargs grep -l Svenska

Giải pháp thông minh; một điều cần lưu ý (nói chung, không liên quan đến những gì OP đang yêu cầu) là mã thoát tổng thể sẽ là 0 ngay cả trong trường hợp (khái niệm) thất bại. Do đó, nếu bạn quan tâm đến việc xác định thất bại so với thành công, bạn phải kiểm tra xem đầu ra của stdout có trống hay không, hoặc sử dụng cách tiếp cận của @ EddSteel để thay thế.
mklement0

@mklement: Trong Bash, PIPESTATUSmảng chứa các giá trị thoát của các thành viên của một đường dẫn.
Tạm dừng cho đến khi có thông báo mới.

@DennisWilliamson Thật là tốt khi biết, cảm ơn bạn. Một tùy chọn khác là bật pipefailtùy chọn shell (tạm thời):shopt -so pipefail
mklement0

4
Bạn có thể muốn sử dụng grep -Zxargs -0nếu tên tệp của bạn có thể chứa khoảng trắng.
Ben Challenor vào

1
Điều này có thể gây ra lỗi "Danh sách đối số quá dài" nếu bạn có nhiều tệp.
AnnanFay

23

Tuy nhiên, một cách khác chỉ sử dụng bash và grep:

Đối với một tệp duy nhất 'test.txt':

  grep -q Dansk test.txt && grep -q Norsk test.txt && grep -l Svenska test.txt

Sẽ in ra test.txttệp chứa cả ba (trong bất kỳ kết hợp nào). Hai greps đầu tiên không in bất cứ thứ gì ( -q) và cuối cùng chỉ in tệp nếu hai greps khác đã qua.

Nếu bạn muốn làm điều đó cho mọi tệp trong thư mục:

   cho f trong *; làm grep -q Dansk $ f && grep -q Norsk $ f && grep -l Svenska $ f; làm xong

nhưng sau đó không cần thực hiện grep 3 lần.
kurumi

1
Tôi biết bạn có thể kết hợp các mẫu với -e, nhưng tôi không thể tìm thấy cách kết hợp chỉ trong grep.
Edd Steel

1
Tuyệt quá; re for f ...: use "$f"(dấu ngoặc kép) thay vì chỉ $fđể đảm bảo rằng tên tệp có dấu cách nhúng, v.v. được xử lý chính xác.
mklement0

Ưu điểm của cách tiếp cận này so với @ vmpstr là mã thoát phản ánh chính xác tất cả các cụm từ tìm kiếm được tìm thấy ở đâu hay không.
mklement0

19
grep –irl word1 * | grep –il word2 `cat -` | grep –il word3 `cat -`
  • -i làm cho tìm kiếm không phân biệt chữ hoa chữ thường
  • -r làm cho tìm kiếm tệp đệ quy thông qua các thư mục
  • -l liệt kê danh sách các tệp với từ tìm thấy
  • cat - khiến grep tiếp theo xem qua các tệp được chuyển đến danh sách nó.

1
đây là câu trả lời đơn giản và dễ hiểu nhất, cảm ơn rất hữu ích!
majick

9

Cách grep cho nhiều chuỗi trong tệp trên các dòng khác nhau (Sử dụng ký hiệu ống dẫn):

for file in *;do 
   test $(grep -E 'Dansk|Norsk|Svenska' $file | wc -l) -ge 3 && echo $file
done

Ghi chú:

  1. Nếu bạn sử dụng dấu ngoặc kép ""với grep của mình, bạn sẽ phải thoát khỏi đường ống như thế này: \|để tìm kiếm Dansk, Norsk và Svenska.

  2. Giả sử rằng một dòng chỉ có một ngôn ngữ.

Hướng dẫn: http://www.cyberciti.biz/faq/howto-use-grep-command-in-linux-unix/


Điều đó sẽ không thất bại nếu Dansk Norsk và Svenska đều xuất hiện trên cùng một đường thẳng?
vmpstr

Yeah. Nó sẽ thất bại trong trường hợp đó. Tôi giả định rằng các ngôn ngữ xuất hiện một trên mỗi dòng.
Damodharan R

Nó cũng sẽ được gửi nếu tôi chỉ có Norsk, nhưng trên ba dòng khác nhau.
Benjamin W.

6

Bạn có thể làm điều này thực sự dễ dàng với ack :

ack -l 'cats' | ack -xl 'dogs'
  • -l: trả về danh sách các tệp
  • -x: lấy các tệp từ STDIN (tìm kiếm trước đó) và chỉ tìm kiếm các tệp đó

Và bạn chỉ có thể tiếp tục đường ống cho đến khi bạn chỉ nhận được các tệp bạn muốn.


Khi tôi thử điều này, nó nói Unknown option: x. Có một phiên bản ack nào đó hỗ trợ cờ x này không?
Hassan

4
awk '/Dansk/{a=1}/Norsk/{b=1}/Svenska/{c=1}END{ if (a && b && c) print "0" }' 

sau đó bạn có thể bắt giá trị trả về bằng shell

nếu bạn có Ruby (1.9+)

ruby -0777 -ne 'print if /Dansk/ and /Norsk/ and /Svenka/' file

1
tại khoản awk END của bạn, có thể bạn muốn: if (a && b && c) {exit 0} else {exit 1}, hoặc ngắn gọn hơnexit !(a && b && c)
glenn Jackman

dung dịch ruby ​​của bạn trông không ổn. điều đó sẽ chỉ in các đoạn có chứa tất cả các từ tìm kiếm. câu hỏi là: liệu tệp (nói chung) có chứa tất cả các từ không, ngay cả khi tất cả chúng không xuất hiện trong cùng một đoạn văn.
glenn jackman

cảm ơn. thay đổi nếu toàn bộ tập tin là cần thiết, sau đó phải sử dụng -0777
Kurumi

4

Thao tác này tìm kiếm nhiều từ trong nhiều tệp:

egrep 'abc|xyz' file1 file2 ..filen 

2
Ngoài việc tìm các tệp có cả hai chuỗi, điều này cũng sẽ tìm các tệp chỉ có 'abc' HOẶC 'xyz'. Tôi nghĩ OP đã yêu cầu các tệp có chứa 'abc' VÀ 'xyz'.
Chris Warth

3

Đơn giản:

grep 'word1\|word2\|word3' *

xem bài đăng này để biết thêm thông tin


Tôi sẽ thêm -lcờ, nhưng khác với điều đó, câu trả lời này có vẻ dễ hiểu nhất đối với tôi, trừ khi tôi thiếu thứ gì đó.
xdhmoore

Yep, Nó cũng là hiệu quả hơn vì bạn không xử lý tất cả các dữ liệu trong nhiều ống và các bộ lọc
Moshe Beeri

3
Câu hỏi hỏi về một biểu thức trả về các tệp chứa cả ba thuật ngữ; điều này trả về các dòng (thay vì tên tệp) chứa bất kỳ dòng nào trong ba (thay vì cả ba).
Benjamin W.

2

Đây là sự pha trộn giữa câu trả lời của glenn jackman và kurumi, cho phép một số regex tùy ý thay vì một số từ cố định tùy ý hoặc một tập hợp regex cố định.

#!/usr/bin/awk -f
# by Dennis Williamson - 2011-01-25

BEGIN {
    for (i=ARGC-2; i>=1; i--) {
        patterns[ARGV[i]] = 0;
        delete ARGV[i];
    }
}

{
    for (p in patterns)
        if ($0 ~ p)
            matches[p] = 1
            # print    # the matching line could be printed
}

END {
    for (p in patterns) {
        if (matches[p] != 1)
            exit 1
    }
}

Chạy nó như thế này:

./multigrep.awk Dansk Norsk Svenska 'Language: .. - A.*c' dvdfile.dat

2

Đây là những gì làm việc tốt cho tôi:

find . -path '*/.svn' -prune -o -type f -exec gawk '/Dansk/{a=1}/Norsk/{b=1}/Svenska/{c=1}END{ if (a && b && c) print FILENAME }' {} \;
./path/to/file1.sh
./another/path/to/file2.txt
./blah/foo.php

Nếu tôi chỉ muốn tìm các tệp .sh có ba tệp này, thì tôi có thể sử dụng:

find . -path '*/.svn' -prune -o -type f -name "*.sh" -exec gawk '/Dansk/{a=1}/Norsk/{b=1}/Svenska/{c=1}END{ if (a && b && c) print FILENAME }' {} \;
./path/to/file1.sh

1

Mở rộng câu trả lời awk của @ kurumi, đây là một hàm bash:

all_word_search() {
    gawk '
        BEGIN {
            for (i=ARGC-2; i>=1; i--) {
                search_terms[ARGV[i]] = 0;
                ARGV[i] = ARGV[i+1];
                delete ARGV[i+1];
            }
        }
        {
            for (i=1;i<=NF; i++) 
                if ($i in search_terms) 
                    search_terms[$1] = 1
        }
        END {
            for (word in search_terms) 
                if (search_terms[word] == 0) 
                    exit 1
        }
    ' "$@"
    return $?
}

Sử dụng:

if all_word_search Dansk Norsk Svenska filename; then
    echo "all words found"
else
    echo "not all words found"
fi

1

Tôi đã làm điều đó với hai bước. Tạo danh sách các tệp csv trong một tệp Với sự trợ giúp của các nhận xét trên trang này, tôi đã thực hiện hai bước không cần kịch bản để có được những gì tôi cần. Chỉ cần nhập vào thiết bị đầu cuối:

$ find /csv/file/dir -name '*.csv' > csv_list.txt
$ grep -q Svenska `cat csv_list.txt` && grep -q Norsk `cat csv_list.txt` && grep -l Dansk `cat csv_list.txt`

nó đã làm chính xác những gì tôi cần - in tên tệp chứa cả ba từ.

Cũng lưu ý đến các ký hiệu như `' "


1

Nếu bạn chỉ cần hai cụm từ tìm kiếm, cách tiếp cận dễ đọc nhất được cho là chạy từng tìm kiếm và cắt các kết quả:

 comm -12 <(grep -rl word1 . | sort) <(grep -rl word2 . | sort)

1

Nếu bạn đã cài đặt git

git grep -l --all-match --no-index -e Dansk -e Norsk -e Svenska

--No-index tìm kiếm các tệp trong thư mục hiện tại không được Git quản lý. Vì vậy, lệnh này sẽ hoạt động trong bất kỳ thư mục nào không phân biệt đó có phải là kho lưu trữ git hay không.


0

Tôi đã gặp sự cố này ngày hôm nay và tất cả các dòng một ở đây đều không thành công với tôi vì các tệp chứa khoảng trắng trong tên.

Đây là những gì tôi nghĩ ra đã hoạt động:

grep -ril <WORD1> | sed 's/.*/"&"/' | xargs grep -il <WORD2>
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.