Lệnh Linux: Làm cách nào để 'tìm' chỉ các tệp văn bản?


100

Sau một vài tìm kiếm từ Google, những gì tôi nghĩ ra là:

find my_folder -type f -exec grep -l "needle text" {} \; -exec file {} \; | grep text

điều này rất không hợp thời và xuất ra các văn bản không cần thiết như thông tin kiểu kịch câm. Bất kỳ giải pháp tốt hơn? Tôi có rất nhiều hình ảnh và các tệp nhị phân khác trong cùng một thư mục với rất nhiều tệp văn bản mà tôi cần tìm kiếm.

Câu trả lời:


184

Tôi biết đây là một chuỗi cũ, nhưng tôi tình cờ xem được nó và nghĩ rằng tôi sẽ chia sẻ phương pháp của mình mà tôi thấy là một cách rất nhanh để sử dụng findđể chỉ tìm các tệp không phải nhị phân:

find . -type f -exec grep -Iq . {} \; -print

Các -Itùy chọn để grep nói với nó ngay lập tức bỏ qua tập tin nhị phân và các .tùy chọn cùng với -qsẽ làm cho nó ngay lập tức phù hợp với tập tin văn bản để nó đi rất nhanh. Bạn có thể thay đổi -printthành a -print0cho đường ống vào một xargs -0hoặc thứ gì đó nếu bạn lo lắng về khoảng trắng (cảm ơn vì mẹo, @ lucas.werkmeister!)

Ngoài ra, dấu chấm đầu tiên chỉ cần thiết cho một số phiên bản BSD nhất định findnhư trên OS X, nhưng nó không ảnh hưởng gì nếu bạn luôn có nó ở đó nếu bạn muốn đặt nó trong một bí danh hoặc một cái gì đó.

CHỈNH SỬA : Như @ruslan đã chỉ ra một cách chính xác, -andcó thể bỏ qua vì nó được ngụ ý.


16
Trên Mac OS X, tôi cần thay đổi điều này thành find . -type f -exec grep -Il "" {} \;.
Alec Jacobson

3
Đây là câu trả lời tốt hơn so với peoro vì 1. nó thực sự trả lời câu hỏi 2. Nó không mang lại dương tính giả 3. nó là cách performant hơn
user123444555621

3
Bạn cũng có thể sử dụng find -type f -exec grep -Iq . {} \; -and -printnó có lợi thế là nó giữ các tệp trong đó find; bạn có thể thay thế -printbằng một -exectệp khác chỉ chạy cho tệp văn bản. (Nếu bạn để grepin tên tập tin, bạn sẽ không thể phân biệt tên tập tin với dòng mới trong đó.)
Lucas Werkmeister

1
@ NathanS.Watson-Haigh Không nên, vì nó phải khớp các tệp văn bản ngay lập tức. Bạn có một trường hợp sử dụng cụ thể, bạn có thể chia sẻ?
crudcore

2
find . -type f -exec grep -Il . {} +nhanh hơn nhiều. Hạn chế là nó không thể được mở rộng bởi người khác -execnhư @ lucas.werkmeister đã đề xuất
Henning


10

Tại sao nó không vui? Nếu bạn cần sử dụng nó thường xuyên và không muốn gõ nó mỗi lần, chỉ cần xác định một hàm bash cho nó:

function findTextInAsciiFiles {
    # usage: findTextInAsciiFiles DIRECTORY NEEDLE_TEXT
    find "$1" -type f -exec grep -l "$2" {} \; -exec file {} \; | grep text
}

đặt nó vào của bạn .bashrcvà sau đó chỉ cần chạy:

findTextInAsciiFiles your_folder "needle text"

bất cứ khi nào bạn muốn.


CHỈNH SỬA để phản ánh chỉnh sửa của OP:

nếu bạn muốn loại bỏ thông tin kịch câm, bạn có thể chỉ cần thêm một giai đoạn nữa vào đường ống lọc ra thông tin kịch câm. Điều này sẽ làm các trick, bằng cách lấy chỉ những gì đến trước :: cut -d':' -f1:

function findTextInAsciiFiles {
    # usage: findTextInAsciiFiles DIRECTORY NEEDLE_TEXT
    find "$1" -type f -exec grep -l "$2" {} \; -exec file {} \; | grep text | cut -d ':' -f1
}

Tôi không chắc liệu "grep text" có đủ chính xác để lấy chính xác tất cả các tệp văn bản hay không - Ý tôi là, có loại tệp văn bản nào không có 'văn bản' trong chuỗi mô tả kiểu kịch câm của nó không?
datasn.io

@ kavoir.com: vâng. Từ filehướng dẫn sử dụng: "Người dùng phụ thuộc vào việc biết rằng tất cả các tệp có thể đọc được trong một thư mục có từ 'văn bản' được in."
peoro

2
Sẽ không thông minh hơn một chút khi tìm kiếm các tệp văn bản trước khi phân loại, thay vì phân tích và sau đó lọc ra các tệp văn bản?
người dùng không xác định

/proc/meminfo, /proc/cpuinfov.v. là các tệp văn bản, nhưng file /proc/meminfonói /proc/meminfo: empty. Tôi tự hỏi liệu có nên kiểm tra "trống" ngoài "văn bản" hay không, nhưng không chắc liệu các loại khác có thể báo cáo "trống" hay không.
Timo Kähkönen

"Tại sao nó không vui?" - "xuất văn bản không cần thiết". Câu trả lời này không phải như vậy.
user123444555621 Ngày

4
find . -type f -print0 | xargs -0 file | grep -P text | cut -d: -f1 | xargs grep -Pil "search"

Điều này không may là không tiết kiệm không gian. Đưa nó vào tập lệnh bash làm cho nó dễ dàng hơn một chút.

Đây là không gian an toàn:

#!/bin/bash
#if [ ! "$1" ] ; then
    echo "Usage: $0 <search>";
    exit
fi

find . -type f -print0 \
  | xargs -0 file \
  | grep -P text \
  | cut -d: -f1 \
  | xargs -i% grep -Pil "$1" "%"

2
Có một số vấn đề trong tập lệnh của bạn: 1. điều gì sẽ xảy ra nếu một tệp nhị phân được đặt tên text.bin? 2. Điều gì xảy ra nếu tên tệp chứa một :?
thkala

3

Một cách khác để làm điều này:

# find . |xargs file {} \; |grep "ASCII text"

Nếu bạn cũng muốn các tệp trống:

#  find . |xargs file {} \; |egrep "ASCII text|empty"

2

Còn cái này thì sao:

$ grep -rl "needle text" my_folder | tr '\n' '\0' | xargs -r -0 file | grep -e ':[^:]*text[^:]*$' | grep -v -e 'executable'

Nếu bạn muốn tên tệp không có loại tệp, chỉ cần thêm sedbộ lọc cuối cùng .

$ grep -rl "needle text" my_folder | tr '\n' '\0' | xargs -r -0 file | grep -e ':[^:]*text[^:]*$' | grep -v -e 'executable' | sed 's|:[^:]*$||'

Bạn có thể lọc ra các loại tệp không cần thiết bằng cách thêm nhiều -e 'type'tùy chọn hơn vào greplệnh cuối cùng .

BIÊN TẬP:

Nếu xargsphiên bản của bạn hỗ trợ -dtùy chọn, các lệnh trên sẽ trở nên đơn giản hơn:

$ grep -rl "needle text" my_folder | xargs -d '\n' -r file | grep -e ':[^:]*text[^:]*$' | grep -v -e 'executable' | sed 's|:[^:]*$||'

tôi thật ngốc. Không nhận thấy grep đệ quy. như tôi hiểu nó thực sự khá nhanh mặc dù có một chút hạn chế trong nhiều ứng dụng. +1 cho bạn.
Antti Rytsölä

2

Đây là cách tôi đã thực hiện nó ...

1. tạo một tập lệnh nhỏ để kiểm tra xem tệp có phải là văn bản thuần túy không:

#!/bin/bash
[[ "$(file -bi $1)" == *"file"* ]]

2. sử dụng tìm như trước đây

find . -type f -exec istext {} \; -exec grep -nHi mystring {} \;

Tôi đoán bạn có nghĩa là == *"text"* ]]?
người dùng không xác định

Bạn có thể sử dụng toán tử so khớp `= ~" text "]]` để thay thế.
người dùng không xác định

2

Tôi có hai vấn đề với câu trả lời của lịch sử:

  • Nó chỉ liệt kê các tệp văn bản. Nó không thực sự tìm kiếm chúng theo yêu cầu. Để thực sự tìm kiếm, hãy sử dụng

    find . -type f -exec grep -Iq . {} \; -and -print0 | xargs -0 grep "needle text"
    
  • Nó tạo ra một quá trình grep cho mọi tệp, quá trình này rất chậm. Một giải pháp tốt hơn sau đó là

    find . -type f -print0 | xargs -0 grep -IZl . | xargs -0 grep "needle text"
    

    hoặc đơn giản

    find . -type f -print0 | xargs -0 grep -I "needle text"
    

    Điều này chỉ mất 0,2 giây so với 4 giây cho giải pháp trên (2,5 GB dữ liệu / 7700 tệp), tức là nhanh hơn 20 lần .

Ngoài ra, không ai trích dẫn ag, Silver Searcher hoặc ack-grep ¸ những lựa chọn thay thế. Nếu một trong số này có sẵn, chúng là những lựa chọn thay thế tốt hơn nhiều:

ag -t "needle text"    # Much faster than ack
ack -t "needle text"   # or ack-grep

Lưu ý cuối cùng, hãy cẩn thận với các trường hợp dương tính giả (các tệp nhị phân được coi là tệp văn bản). Tôi đã có kết quả dương tính giả khi sử dụng grep / ag / ack, vì vậy tốt hơn hết hãy liệt kê các tệp phù hợp trước khi chỉnh sửa tệp.


1

Mặc dù đây là một câu hỏi cũ, nhưng tôi nghĩ thông tin dưới đây sẽ làm tăng thêm chất lượng của các câu trả lời ở đây.

Khi bỏ qua các tệp có bộ bit thực thi , tôi chỉ sử dụng lệnh này:

find . ! -perm -111

Để giữ nó không nhập đệ quy vào các thư mục khác:

find . -maxdepth 1 ! -perm -111

Không cần đường ống kết hợp nhiều lệnh, chỉ cần lệnh tìm đơn giản mạnh mẽ .

  • Tuyên bố từ chối trách nhiệm: nó không chính xác như những gì OP yêu cầu, vì nó không kiểm tra xem tệp có phải là tệp nhị phân hay không. Ví dụ, nó sẽ lọc ra các tệp kịch bản bash , bản thân là văn bản nhưng có bộ bit thực thi .

Điều đó nói rằng, tôi hy vọng điều này hữu ích cho bất kỳ ai.


0

Tôi thực hiện theo cách này: 1) vì có quá nhiều tệp (~ 30k) để tìm kiếm, tôi tạo danh sách tệp văn bản hàng ngày để sử dụng qua crontab bằng lệnh dưới đây:

find /to/src/folder -type f -exec file {} \; | grep text | cut -d: -f1 > ~/.src_list &

2) tạo một hàm trong .bashrc:

findex() {
    cat ~/.src_list | xargs grep "$*" 2>/dev/null
}

Sau đó, tôi có thể sử dụng lệnh dưới đây để thực hiện tìm kiếm:

findex "needle text"

HTH :)


0

Tôi thích xargs hơn

find . -type f | xargs grep -I "needle text"

nếu tên tệp của bạn lạ, hãy tra cứu bằng cách sử dụng tùy chọn -0:

find . -type f -print0 | xargs -0 grep -I "needle text"

0
  • bash ví dụ để tìm kiếm văn bản "eth0" trong / etc trong tất cả các tệp văn bản / ascii

grep eth0 $ (find / etc / -type f -exec file {} \; | egrep -i "text | ascii" | cut -d ':' -f1)


0

Đây là một phiên bản đơn giản với giải thích mở rộng cho những người mới bắt đầu như tôi, những người đang cố gắng học cách đặt nhiều lệnh trong một dòng.

Nếu bạn viết ra vấn đề theo từng bước, nó sẽ giống như sau:

// For every file in this directory
// Check the filetype
// If it's an ASCII file, then print out the filename

Để đạt được điều này, chúng ta có thể sử dụng ba lệnh UNIX: find, file, và grep.

find sẽ kiểm tra mọi tệp trong thư mục.

filesẽ cung cấp cho chúng tôi loại tệp. Trong trường hợp của chúng tôi, chúng tôi đang tìm kiếm sự trả lại của 'văn bản ASCII'

grep sẽ tìm kiếm từ khóa 'ASCII' trong đầu ra từ file

Vậy làm thế nào chúng ta có thể xâu chuỗi chúng lại với nhau trong một dòng? Có nhiều cách để làm điều đó, nhưng tôi thấy rằng làm điều đó theo thứ tự mã giả của chúng tôi có ý nghĩa nhất (đặc biệt là đối với một người mới bắt đầu như tôi).

find ./ -exec file {} ";" | grep 'ASCII'

Trông phức tạp, nhưng không tệ khi chúng ta chia nhỏ nó ra:

find ./= xem qua mọi tệp trong thư mục này. Các findlệnh in ra tên tập tin của bất kỳ tập tin phù hợp với 'biểu hiện', hoặc bất cứ điều gì xảy ra sau khi con đường, mà trong trường hợp của chúng tôi là thư mục hiện tại hoặc./

Điều quan trọng nhất cần hiểu là mọi thứ sau bit đầu tiên đó sẽ được đánh giá là Đúng hoặc Sai. Nếu Đúng, tên tệp sẽ được in ra. Nếu không, thì lệnh sẽ tiếp tục.

-exec= cờ này là một tùy chọn trong lệnh find cho phép chúng ta sử dụng kết quả của một số lệnh khác làm biểu thức tìm kiếm. Nó giống như gọi một hàm trong một hàm.

file {}= lệnh được gọi bên trong của find. Các filelệnh trả về một chuỗi cho bạn biết filetype của một tập tin. Thường xuyên, nó sẽ trông như thế này: file mytextfile.txt. Trong trường hợp của chúng tôi, chúng tôi muốn nó sử dụng bất kỳ tệp nào đang được lệnh xem xét find, vì vậy chúng tôi đặt vào dấu ngoặc nhọn {}để hoạt động như một biến trống hoặc tham số. Nói cách khác, chúng tôi chỉ yêu cầu hệ thống xuất một chuỗi cho mọi tệp trong thư mục.

";"= cái này được yêu cầu bởi findvà là dấu chấm câu ở cuối -execlệnh của chúng ta . Xem hướng dẫn về 'tìm' để biết thêm giải thích nếu bạn cần bằng cách chạy man find.

| grep 'ASCII'= |là một cái ống. Pipe lấy đầu ra của bất kỳ thứ gì ở bên trái và sử dụng nó làm đầu vào cho bất kỳ thứ gì ở bên phải. Nó lấy đầu ra của findlệnh (một chuỗi là kiểu tệp của một tệp duy nhất) và kiểm tra nó để xem nó có chứa chuỗi hay không 'ASCII'. Nếu đúng, nó trả về true.

NOW, biểu thức ở bên phải của find ./sẽ trả về true khi greplệnh trả về true. Thì đấy.


0

Nếu bạn quan tâm đến việc tìm kiếm bất kỳ loại tệp nào theo byte kỳ diệu của chúng bằng cách sử dụng filetiện ích tuyệt vời kết hợp với sức mạnh của find, điều này có thể hữu ích:

$ # Let's make some test files
$ mkdir ASCII-finder
$ cd ASCII-finder
$ dd if=/dev/urandom of=binary.file bs=1M count=1
1+0 records in
1+0 records out
1048576 bytes (1.0 MB, 1.0 MiB) copied, 0.009023 s, 116 MB/s
$ file binary.file
binary.file: data
$ echo 123 > text.txt
$ # Let the magic begin
$ find -type f -print0 | \
    xargs -0 -I @@ bash -c 'file "$@" | grep ASCII &>/dev/null && echo "file is ASCII: $@"' -- @@

Đầu ra:

file is ASCII: ./text.txt

Chú giải: $là dấu nhắc trình bao tương tác nơi chúng ta nhập các lệnh của mình

Bạn có thể sửa đổi phần sau &&để gọi một số tập lệnh khác hoặc thực hiện một số nội tuyến khác, tức là nếu tệp đó chứa chuỗi đã cho, hãy tạo toàn bộ tệp hoặc tìm kiếm một chuỗi phụ trong đó.

Giải trình:

  • find các mục là tệp
  • Tạo xargsnguồn cấp dữ liệu từng mục dưới dạng một dòng thành một bash lệnh / tập lệnh lót
  • filekiểm tra loại tệp theo byte ma thuật, grepkiểm tra xem ASCII có tồn tại hay không, nếu có thì sau khi &&lệnh tiếp theo của bạn thực thi.
  • findin kết quả nulltách biệt, điều này rất tốt để thoát khỏi tên tệp có khoảng trắng và ký tự meta trong đó.
  • xargs, sử dụng -0tùy chọn, đọc chúng được nulltách biệt, -I @@ lấy từng bản ghi và sử dụng làm tham số vị trí / args để bash script.
  • --bashđảm bảo bất cứ điều gì xuất hiện sau nó là một đối số ngay cả khi nó bắt đầu bằng -like -cmà nếu không thì có thể được hiểu là tùy chọn bash

Nếu bạn cần tìm các loại không phải ASCII, chỉ cần thay thế grep ASCIIbằng loại khác, nhưgrep "PDF document, version 1.4"


-1
find . -type f | xargs file | grep "ASCII text" | awk -F: '{print $1}'

Sử dụng lệnh find để liệt kê tất cả các tệp, sử dụng lệnh tệp để xác minh chúng là văn bản (không phải tar, key), cuối cùng sử dụng lệnh awk để lọc và in kết quả.


-4

Còn cái này thì sao

 find . -type f|xargs grep "needle text"

Điều này không tìm kiếm"needle text"
peoro

@Navi: ví dụ OP chỉ cung cấp tìm thấy file chứa"needl text"
peoro

3
@Navi: bây giờ nó không tìm kiếm tập tin văn bản nữa: nếu một tập tin nhị phân chứa "needle text"nó sẽ được tìm thấy
peoro

Tại sao tôi thậm chí còn lắng nghe bạn?
Navi

1
@Navi: one-liner của bạn không kiểm tra các loại tập tin và cũng có vấn đề lớn với khoảng trắng trong tên tập tin ...
thkala
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.