Làm cách nào để sử dụng tùy chọn '-prune' của 'find' trong sh?


219

Tôi hoàn toàn không hiểu ví dụ được đưa ra từ man find, ai có thể cho tôi một số ví dụ và giải thích không? Tôi có thể kết hợp biểu thức chính quy trong đó không?


Câu hỏi chi tiết hơn là như thế này:

Viết một kịch bản shell changeall, có giao diện như thế nào changeall [-r|-R] "string1" "string2". Nó sẽ tìm thấy tất cả các file đi kèm hậu tố của .h, .C, .cc, hay .cppvà thay đổi tất cả các lần xuất hiện của string1để string2. -rlà tùy chọn chỉ ở trong thư mục hiện tại hoặc bao gồm cả thư mục con.

GHI CHÚ:

  1. Đối với trường hợp không đệ quy, lsKHÔNG được phép, chúng tôi chỉ có thể sử dụng findsed.
  2. Tôi đã thử find -depthnhưng nó KHÔNG được hỗ trợ. Đó là lý do tại sao tôi tự hỏi nếu -prunecó thể giúp đỡ, nhưng không hiểu ví dụ từ man find.

EDIT2: Tôi đang làm bài tập, tôi không đặt câu hỏi rất chi tiết vì tôi muốn tự mình hoàn thành nó. Vì tôi đã thực hiện nó và đưa nó vào, bây giờ tôi có thể nêu toàn bộ câu hỏi. Ngoài ra, tôi quản lý để hoàn thành bài tập mà không sử dụng -prune, nhưng dù sao cũng muốn học nó.

Câu trả lời:


438

Điều tôi thấy khó hiểu -prunelà đó là một hành động (như -print), không phải là một thử nghiệm (như -name). Nó thay đổi danh sách "việc cần làm", nhưng luôn trả về đúng .

Mẫu chung để sử dụng -prunelà:

find [path] [conditions to prune] -prune -o \
            [your usual conditions] [actions to perform]

Bạn luôn luôn muốn -o(logic OR) ngay lập tức sau đó -prune, bởi vì phần đầu tiên của bài kiểm tra (tối đa và bao gồm -prune) sẽ trả về sai cho những thứ bạn thực sự muốn (ví dụ: những thứ bạn không muốn cắt bỏ).

Đây là một ví dụ:

find . -name .snapshot -prune -o -name '*.foo' -print

Điều này sẽ tìm thấy các tệp "* .foo" không có trong thư mục ".snapshot". Trong ví dụ này, -name .snapshottạo nên [conditions to prune], và -name '*.foo' -print[your usual conditions][actions to perform].

Lưu ý quan trọng :

  1. Nếu tất cả những gì bạn muốn làm là in kết quả mà bạn có thể được sử dụng để loại bỏ -printhành động. Bạn thường không muốn làm điều đó khi sử dụng -prune.

    Hành vi mặc định của find là "và" toàn bộ biểu thức với -printhành động nếu không có hành động nào khác ngoài -prune(trớ trêu thay) ở cuối. Điều đó có nghĩa là viết điều này:

    find . -name .snapshot -prune -o -name '*.foo'              # DON'T DO THIS

    tương đương với việc viết này:

    find . \( -name .snapshot -prune -o -name '*.foo' \) -print # DON'T DO THIS

    điều đó có nghĩa là nó cũng sẽ in ra tên của thư mục bạn đang cắt tỉa, thường không phải là thứ bạn muốn. Thay vào đó, tốt hơn là chỉ định rõ ràng -printhành động nếu đó là điều bạn muốn:

    find . -name .snapshot -prune -o -name '*.foo' -print       # DO THIS
  2. Nếu "điều kiện thông thường" của bạn xảy ra khớp với các tệp cũng khớp với điều kiện cắt tỉa của bạn, thì các tệp đó sẽ không được đưa vào đầu ra. Cách để khắc phục điều này là thêm một-type d vị ngữ vào điều kiện cắt tỉa của bạn.

    Ví dụ: giả sử chúng tôi muốn loại bỏ bất kỳ thư mục nào bắt đầu .git(điều này được thừa nhận là hơi bị chiếm đoạt - thông thường bạn chỉ cần xóa thứ có tên chính xác .git ), nhưng ngoài việc muốn xem tất cả các tệp, bao gồm các tệp như .gitignore. Bạn có thể thử điều này:

    find . -name '.git*' -prune -o -type f -print               # DON'T DO THIS

    Điều này sẽ không bao gồm .gitignoretrong đầu ra. Đây là phiên bản cố định:

    find . -name '.git*' -type d -prune -o -type f -print       # DO THIS

Mẹo thêm: nếu bạn đang sử dụng phiên bản GNU của find, trang texinfo cho findcó một lời giải thích chi tiết hơn so với trang của nó (như đúng với hầu hết các tiện ích GNU).


6
nó không rõ ràng 100% trong văn bản của bạn (nhưng vì bạn chỉ in '* .foo' nó không xung đột) nhưng phần -prune cũng sẽ không in bất cứ thứ gì (không chỉ thư mục) có tên ".snapshot". tức là, -prunekhông chỉ hoạt động trên các thư mục (nhưng, đối với các thư mục, nó cũng không ngăn được việc nhập các thư mục phù hợp với điều kiện đó, tức là ở đây các thư mục phù hợp với điều đó -name .snapshot).
Olivier Dulac

12
và +1 cho bạn để giải thích được thực hiện độc đáo (và đặc biệt là ghi chú quan trọng). Bạn nên gửi nó cho những người phát triển tìm kiếm (vì trang nam không giải thích "cắt tỉa" cho con người bình thường ^^ Tôi đã mất nhiều lần cố gắng để tìm ra nó và tôi không thấy tác dụng phụ mà bạn cảnh báo chúng tôi)
Olivier Dulac

2
@OlivierDulac Đó là một điểm rất hay về khả năng tước các tệp bạn muốn giữ. Tôi đã cập nhật câu trả lời để làm rõ điều này. Nhân tiện, nó không phải -prunelà nguyên nhân gây ra điều này. Vấn đề là hoặc toán tử "ngắn mạch", hoặc có mức độ ưu tiên thấp hơn và. Kết quả cuối cùng là nếu .snapshotgặp phải một tệp được gọi, nó sẽ khớp với tệp đầu tiên -name, -prunesau đó sẽ không làm gì (nhưng trả về true), và sau đó hoặc trả về true vì đối số bên trái của nó là đúng. Hành động (ví dụ -print:) là một phần của đối số thứ hai của nó, vì vậy nó không bao giờ có cơ hội thực thi.
Laurence Gonsalves

3
+1 cuối cùng đã tìm ra lý do tại sao tôi cần -printcuối cùng, bây giờ tôi có thể ngừng thêm \! -path <pattern>vào-prune
Biến số đáng thương

6
Lưu ý rằng "-o" là viết tắt của "-hoặc", trong đó (trong khi không tuân thủ POSIX) đọc rõ hơn.
yoyo

27

Thông thường cách chúng ta làm mọi thứ trong linux và cách chúng ta nghĩ là từ trái sang phải.
Vì vậy, bạn sẽ đi và viết những gì bạn đang tìm kiếm đầu tiên:

find / -name "*.php"

Sau đó, bạn có thể nhấn enter và nhận ra rằng bạn đang nhận quá nhiều tệp từ các thư mục bạn không muốn. Hãy loại trừ / phương tiện để tránh tìm kiếm ổ đĩa gắn kết của bạn.
Bây giờ bạn chỉ cần PHỤ LỤC các lệnh sau với lệnh trước:

-print -o -path '/media' -prune

Vì vậy, lệnh cuối cùng là:

find / -name "*.php" -print -o -path '/media' -prune

............... | <--- Bao gồm ---> | .................... | <- -------- Không bao gồm ---------> |

Tôi nghĩ rằng cấu trúc này dễ dàng hơn nhiều và tương quan với cách tiếp cận đúng


3
Tôi không mong đợi điều này sẽ hiệu quả - tôi đã nghĩ rằng nó sẽ đánh giá mệnh đề bên trái trước khi cắt tỉa, nhưng thật ngạc nhiên, một thử nghiệm nhanh dường như cho thấy findđủ thông minh để xử lý -prunemệnh đề trước. Hừm, thú vị.
artfulrobot

Tôi chưa bao giờ nghĩ rằng trong gần một thập kỷ sử dụng GNU find! Cảm ơn vì điều đó! Nó chắc chắn sẽ thay đổi cách tôi nghĩ về -prunetừ bây giờ.
Felipe Alvarez

3
@artfulrobot Có thực sự xử lý nó trước không? Tôi đã nghĩ rằng nó đang xâm nhập /media, nhận thấy rằng nó không được gọi *.phpvà sau đó kiểm tra xem nó có ở bên trong không /media, thấy rằng nó đang tồn tại và do đó bỏ qua toàn bộ cây con đó. Nó vẫn từ trái sang phải, nó chỉ không có sự khác biệt miễn là cả hai kiểm tra không trùng nhau.
phk

26

Coi chừng rằng -prune không ngăn chặn việc đi xuống bất kỳ thư mục nào như một số người đã nói. Nó ngăn chặn giảm dần vào các thư mục phù hợp với thử nghiệm mà nó được áp dụng. Có lẽ một số ví dụ sẽ giúp (xem phía dưới cho một ví dụ regex). Xin lỗi vì điều này quá dài.

$ find . -printf "%y %p\n"    # print the file type the first time FYI
d .
f ./test
d ./dir1
d ./dir1/test
f ./dir1/test/file
f ./dir1/test/test
d ./dir1/scripts
f ./dir1/scripts/myscript.pl
f ./dir1/scripts/myscript.sh
f ./dir1/scripts/myscript.py
d ./dir2
d ./dir2/test
f ./dir2/test/file
f ./dir2/test/myscript.pl
f ./dir2/test/myscript.sh

$ find . -name test
./test
./dir1/test
./dir1/test/test
./dir2/test

$ find . -prune
.

$ find . -name test -prune
./test
./dir1/test
./dir2/test

$ find . -name test -prune -o -print
.
./dir1
./dir1/scripts
./dir1/scripts/myscript.pl
./dir1/scripts/myscript.sh
./dir1/scripts/myscript.py
./dir2

$ find . -regex ".*/my.*p.$"
./dir1/scripts/myscript.pl
./dir1/scripts/myscript.py
./dir2/test/myscript.pl

$ find . -name test -prune -regex ".*/my.*p.$"
(no results)

$ find . -name test -prune -o -regex ".*/my.*p.$"
./test
./dir1/test
./dir1/scripts/myscript.pl
./dir1/scripts/myscript.py
./dir2/test

$ find . -regex ".*/my.*p.$" -a -not -regex ".*test.*"
./dir1/scripts/myscript.pl
./dir1/scripts/myscript.py

$ find . -not -regex ".*test.*"                   .
./dir1
./dir1/scripts
./dir1/scripts/myscript.pl
./dir1/scripts/myscript.sh
./dir1/scripts/myscript.py
./dir2

nếu bạn cũng "chạm vào ./dir1/scripts/test" (nghĩa là có tệp "kiểm tra" chứ không phải dir, trong thư mục con được in ra), nó sẽ không được in bởi find . -name test -prune -o -print: iow, -prunecũng là một hành động hoạt động trên các tệp
Olivier Dulac

10

Thêm vào lời khuyên được đưa ra trong các câu trả lời khác (Tôi không có đại diện để tạo câu trả lời) ...

Khi kết hợp -prunevới các biểu thức khác, có một sự khác biệt tinh tế trong hành vi tùy thuộc vào các biểu thức khác được sử dụng.

Ví dụ của @Laurence Gonsalves sẽ tìm thấy các tệp "* .foo" không có trong thư mục ".snapshot": -

find . -name .snapshot -prune -o -name '*.foo' -print

Tuy nhiên, bàn tay hơi khác biệt này, có lẽ vô tình, cũng liệt kê .snapshotthư mục (và bất kỳ thư mục .snapshot lồng nhau nào): -

find . -name .snapshot -prune -o -name '*.foo'

Lý do là (theo trang chủ trên hệ thống của tôi): -

Nếu biểu thức đã cho không chứa bất kỳ biểu thức gốc nào -exec, -ls, -ok hoặc -print, biểu thức đã cho được thay thế một cách hiệu quả bằng:

(give_expression) -print

Đó là, ví dụ thứ hai tương đương với việc nhập nội dung sau, do đó sửa đổi nhóm các thuật ngữ: -

find . \( -name .snapshot -prune -o -name '*.foo' \) -print

Điều này ít nhất đã được nhìn thấy trên Solaris 5.10. Đã sử dụng nhiều hương vị khác nhau của * nix trong khoảng 10 năm, tôi mới chỉ tìm kiếm lý do tại sao điều này xảy ra.


Cảm ơn bạn đã kêu gọi sự chú ý đến sự khác biệt giữa sử dụng -prunecó và không có -print!
mcw

3

Prune là không tái diễn tại bất kỳ chuyển đổi thư mục.

Từ trang người đàn ông

Nếu -depth không được đưa ra, đúng; nếu tập tin là một thư mục, đừng đi vào nó Nếu -depth được đưa ra, sai; không có tác dụng

Về cơ bản nó sẽ không đi vào bất kỳ thư mục phụ.

Lấy ví dụ này:

Bạn có các thư mục sau

  • / nhà / test2
  • / nhà / test2 / test2

Nếu bạn chạy find -name test2:

Nó sẽ trả về cả hai thư mục

Nếu bạn chạy find -name test2 -prune:

Nó sẽ chỉ trả về / home / test2 vì nó sẽ không xuống / home / test2 để tìm / home / test2 / test2


không đúng 100%: đó là "thực hiện cắt tỉa khi phù hợp với điều kiện và nếu đó là một thư mục, hãy đưa nó ra khỏi danh sách việc cần làm, tức là đừng nhập nó". -prune cũng hoạt động trên các tập tin.
Olivier Dulac

2

Tôi không phải là chuyên gia về điều này (và trang này rất hữu ích cùng với http://mywiki.wooledge.org/UsingFind )

Chỉ cần chú ý -pathlà một đường dẫn khớp hoàn toàn với chuỗi / đường dẫn xuất hiện ngay saufind ( .trong các ví dụ này) trong đó -namekhớp với tất cả các tên cơ sở.

find . -path ./.git  -prune -o -name file  -print

chặn thư mục .git trong thư mục hiện tại của bạn ( như bạn tìm thấy . )

find . -name .git  -prune -o -name file  -print

chặn tất cả các thư mục con .git đệ quy.

Lưu ý ./ là cực kỳ quan trọng !! -pathphải khớp với một con đường được neo vào . hoặc bất cứ thứ gì xuất hiện ngay sau khi tìm thấy nếu bạn nhận được kết quả khớp với nó (từ phía bên kia của hoặc ' -o') có thể không được cắt tỉa! Tôi đã ngây thơ không biết điều này và nó cho tôi sử dụng -path khi thật tuyệt khi bạn không muốn cắt xén tất cả thư mục con với cùng tên cơ sở: D


Lưu ý nếu bạn đang nói find bla/thì bạn sẽ cần -path bla/.git (hoặc nếu bạn đẩy một cái *ở phía trước thay vì nó sẽ hoạt động giống như -name)
sabgenton

1

Hiển thị tất cả mọi thứ bao gồm chính dir nhưng không phải là nội dung nhàm chán dài của nó:

find . -print -name dir -prune

0

Nếu bạn đọc tất cả các câu trả lời tốt ở đây, sự hiểu biết của tôi bây giờ là tất cả những điều sau đây đều trả về cùng một kết quả:

find . -path ./dir1\*  -prune -o -print

find . -path ./dir1  -prune -o -print

find . -path ./dir1\*  -o -print
#look no prune at all!

Nhưng cái cuối cùng sẽ mất nhiều thời gian hơn vì nó vẫn tìm kiếm mọi thứ trong dir1. Tôi đoán câu hỏi thực sự là làm thế nào để -orđưa ra kết quả không mong muốn mà không thực sự tìm kiếm chúng.

Vì vậy, tôi đoán prune có nghĩa là không phù hợp với quá khứ phù hợp nhưng đánh dấu nó là xong ...

http://www.gnu.org/software/findutils/manual/html_mono/find.html "Tuy nhiên, điều này không phải do tác động của hành động '-prune' (chỉ ngăn chặn sự xuống dốc thêm nữa, nó không chắc chắn chúng tôi bỏ qua mục đó). Thay vào đó, hiệu ứng này là do việc sử dụng '-o'. Vì phía bên trái của điều kiện của dòng Điên hoặc đã thành công cho ./src/emacs, không cần thiết phải đánh giá bên phải- bên tay ('-print') hoàn toàn cho tập tin cụ thể này. "


0

findxây dựng một danh sách các tập tin. Nó áp dụng vị ngữ bạn đã cung cấp cho từng người và trả về những vị trí vượt qua.

Ý tưởng này -prunecó nghĩa là loại trừ khỏi kết quả thực sự khó hiểu đối với tôi. Bạn có thể loại trừ một tệp mà không cần cắt tỉa:

find -name 'bad_guy' -o -name 'good_guy' -print  // good_guy

Tất cả -prunekhông thay đổi hành vi tìm kiếm. Nếu trận đấu hiện tại là một thư mục, nó sẽ nói "này find, tập tin bạn vừa khớp, không đi vào nó" . Nó chỉ loại bỏ cây đó (chứ không phải chính tệp) khỏi danh sách các tệp cần tìm.

Nó nên được đặt tên -dont-descend.


0

Có khá nhiều câu trả lời; một số trong số họ là quá nhiều lý thuyết nặng. Tôi sẽ để lại lý do tại sao tôi cần cắt tỉa một lần để có thể loại giải thích cần-trước / ví dụ hữu ích cho ai đó :)

Vấn đề

Tôi đã có một thư mục với khoảng 20 thư mục nút, mỗi thư mục có thư mục node_modulesnhư mong đợi.

Một khi bạn nhận được vào bất kỳ dự án, bạn nhìn thấy từng ../node_modules/module. Nhưng bạn biết nó như thế nào. Hầu như mọi mô-đun đều có phụ thuộc, vì vậy những gì bạn đang xem giống nhưprojectN/node_modules/moduleX/node_modules/moduleZ...

Tôi không muốn chết đuối với một danh sách với sự phụ thuộc của sự phụ thuộc của ...

Biết -d n/ -depth n, nó sẽ không giúp tôi, vì thư mục node_modules chính / đầu tiên tôi muốn của mỗi dự án ở một độ sâu khác nhau, như thế này:

Projects/MysuperProjectName/project/node_modules/...
Projects/Whatshisname/version3/project/node_modules/...
Projects/project/node_modules/...
Projects/MysuperProjectName/testProject/november2015Copy/project/node_modules/...
[...]

Làm thế nào tôi có thể nhận được danh sách đầu tiên các đường dẫn kết thúc ở đầu tiên node_modulesvà chuyển sang dự án tiếp theo để có cùng một đường dẫn?

Đi vào -prune

Khi bạn thêm -prune, bạn vẫn sẽ có một tìm kiếm đệ quy tiêu chuẩn. Mỗi "con đường" được phân tích, và mọi phát hiện đều được nhổ ra và findtiếp tục đào xuống như một chap tốt. Nhưng đó là đào sâu để biết thêm node_modulesnhững gì tôi không muốn.

Vì vậy, sự khác biệt là trong bất kỳ con đường nào khác nhau, -prunesẽ findngừng đào sâu xuống con đường cụ thể đó khi nó đã tìm thấy vật phẩm của bạn. Trong trường hợp của tôi, node_modulesthư mục.

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.