Giới hạn POSIX tìm đến độ sâu cụ thể?


15

Gần đây tôi nhận thấy rằng các thông số kỹ thuật POSIXfind không bao gồm -maxdepthchính.

Đối với những người không quen thuộc với nó, mục đích của -maxdepthchính là hạn chế bao nhiêu cấp độ sâu findsẽ hạ xuống. -maxdepth 0kết quả chỉ đối số dòng lệnh đang được xử lý; -maxdepth 1sẽ chỉ xử lý kết quả trực tiếp trong các đối số dòng lệnh, v.v.

Làm cách nào tôi có thể có hành vi tương đương với chính không phải POSIX -maxdepthchỉ bằng các công cụ và tùy chọn do POSIX chỉ định?

(Lưu ý: Tất nhiên tôi có thể nhận được tương đương -maxdepth 0bằng cách chỉ sử dụng -prunelàm toán hạng đầu tiên, nhưng điều đó không mở rộng đến các độ sâu khác.)


Cách tiếp cận @StevenPenny, FreeBSD -depth -2, -depth 1... có thể được xem là tốt hơn so với GNU -maxdepth/-mindepth
Stéphane Chazelas

@ StéphaneChazelas bằng mọi cách - Tìm kiếm POSIX nên có cái này hoặc cái kia; mặt khác nó bị tê liệt
Steven Penny

1
Ít nhất là cho -maxdepth/ -mindepth, có những lựa chọn thay thế hợp lý (lưu ý rằng đây -pathlà một bổ sung gần đây cho POSIX). Các lựa chọn thay thế cho -timexyhoặc -mtime -3m(hoặc -mmin -3) cồng kềnh hơn rất nhiều. Một số thích -execdir/ -deletekhông có sự thay thế đáng tin cậy.
Stéphane Chazelas

2
@StevenPenny, vui lòng đăng nhập một vé tại austingroupbugs.net để yêu cầu thêm nó. Tôi đã thấy mọi thứ được thêm vào mà không cần nhà tài trợ khi có sự biện minh mạnh mẽ. Một cách hành động có lẽ tốt hơn sẽ là có được nhiều triển khai thêm vào trước để POSIX chỉ cần xác định cái hiện có thường ít gây tranh cãi.
Stéphane Chazelas

@ StéphaneChazelas trong trường hợp của tôi, cuối cùng tôi chỉ đặt tên các tệp trực tiếp, nhưng cảm ơn bạn; Tôi có thể nộp vé nếu điều này xuất hiện trở lại
Steven Penny

Câu trả lời:


7

Bạn có thể sử dụng -pathđể phù hợp với độ sâu nhất định và cắt tỉa ở đó. Ví dụ

find . -path '*/*/*' -prune -o -type d -print

sẽ là tối đa 1, khi các *trận đấu ., */*trận đấu ./dir1*/*/*trận đấu ./dir1/dir2được cắt tỉa. Nếu bạn sử dụng một thư mục bắt đầu tuyệt đối, bạn cần thêm một dẫn /đến -pathquá.


Hừm, gian xảo. Bạn không thể loại bỏ một lớp /*từ cuối mẫu, loại bỏ -otoán tử và nhận được kết quả tương tự?
tự đại diện

Không, bởi vì *trận đấu /là tốt, vì vậy, dir a/b/c/d/esẽ phù hợp -path */*, đáng buồn.
meuh

Nhưng a/b/c/d/esẽ không bao giờ đạt được , bởi vì -prunesẽ được áp dụng cho a/b....
Wildcard

1
Xin lỗi, tôi đã đọc sai -prune-ođã được gỡ bỏ. Nếu bạn giữ -prunevấn đề là cái đó */*sẽ không khớp với bất cứ thứ gì ở mức cao hơn maxdepth, ví dụ như thư mục đơn a.
meuh

11

Cách tiếp cận của @ meuh không hiệu quả vì -maxdepth 1cách tiếp cận của anh ấy vẫn cho phép findđọc nội dung của các thư mục ở cấp 1 để sau đó bỏ qua chúng. Nó cũng sẽ không hoạt động đúng với một số findtriển khai (bao gồm GNU find) nếu một số tên thư mục chứa chuỗi byte không tạo thành các ký tự hợp lệ trong miền địa phương của người dùng (như đối với tên tệp trong mã hóa ký tự khác).

find . \( -name . -o -prune \) -extra-conditions-and-actions

là cách thức kinh điển hơn để triển khai GNU -maxdepth 1(hoặc FreeBSD -depth -2).

Nói chung, mặc dù, đó là điều -depth 1bạn muốn ( -mindepth 1 -maxdepth 1) vì bạn không muốn xem xét .(độ sâu 0), và sau đó nó thậm chí còn đơn giản hơn:

find . ! -name . -prune -extra-conditions-and-actions

-maxdepth 2, điều đó trở thành:

find . \( ! -path './*/*' -o -prune \) -extra-conditions-and-actions

Và đó là nơi bạn chạy trong các vấn đề nhân vật không hợp lệ.

Chẳng hạn, nếu bạn có một thư mục được gọi Stéphanenhưng éđược mã hóa trong bộ ký tự iso8859-1 (còn gọi là latin1) (0xe9 byte) như phổ biến nhất ở Tây Âu và Mỹ cho đến giữa những năm 2000, thì byte 0xe9 đó không phải là một ký tự hợp lệ trong UTF-8. Vì vậy, trong các ngôn ngữ UTF-8, *ký tự đại diện (với một số findtriển khai) sẽ không khớp Stéphanevới *0 hoặc nhiều ký tự và 0xe9 không phải là ký tự.

$ locale charmap
UTF-8
$ find . -maxdepth 2
.
./St?phane
./St?phane/Chazelas
./Stéphane
./Stéphane/Chazelas
./John
./John/Smith
$ find . \( ! -path './*/*' -o -prune \)
.
./St?phane
./St?phane/Chazelas
./St?phane/Chazelas/age
./St?phane/Chazelas/gender
./St?phane/Chazelas/address
./Stéphane
./Stéphane/Chazelas
./John
./John/Smith

My find(khi đầu ra đi đến một thiết bị đầu cuối) hiển thị byte 0xe9 không hợp lệ như ?trên. Bạn có thể thấy đó St<0xe9>phane/Chazelaskhông phải là pruned.

Bạn có thể làm việc xung quanh nó bằng cách làm:

LC_ALL=C find . \( ! -path './*/*' -o -prune \) -extra-conditions-and-actions

Nhưng lưu ý rằng điều đó ảnh hưởng đến tất cả các cài đặt ngôn ngữ findvà bất kỳ ứng dụng nào nó chạy (như thông qua các -execvị từ).

$ LC_ALL=C find . \( ! -path './*/*' -o -prune \)
.
./St?phane
./St?phane/Chazelas
./St??phane
./St??phane/Chazelas
./John
./John/Smith

Bây giờ, tôi thực sự nhận được một -maxdepth 2lưu ý rằng é trong Stéphane thứ hai được mã hóa chính xác trong UTF-8 được hiển thị dưới ??dạng các byte 0xc3 0xa9 (được coi là hai ký tự không xác định riêng lẻ trong ngôn ngữ C) của mã hóa UTF-8 không in các ký tự trong miền địa phương C.

Và nếu tôi đã thêm một -name '????????', tôi sẽ nhận được Stéphane sai (cái được mã hóa trong iso8859-1).

Để áp dụng cho các đường dẫn tùy ý thay vì ., bạn sẽ làm:

find some/dir/. ! -name . -prune ...

cho -mindepth 1 -maxdepth 1hoặc:

find some/dir/. \( ! -path '*/./*/*' -o -prune \) ...

cho -maxdepth 2.

Tôi vẫn sẽ làm một:

(cd -P -- "$dir" && find . ...)

Đầu tiên bởi vì điều đó làm cho các đường dẫn ngắn hơn khiến nó ít có khả năng chạy vào đường dẫn quá dài hoặc liệt kê các vấn đề quá dài mà còn khắc phục được thực tế là findkhông thể hỗ trợ các đối số đường dẫn tùy ý (ngoại trừ -fvới FreeBSD find) vì nó sẽ bị nghẹt giá trị $dirthích !hoặc -print...


Sự -okết hợp với phủ định là một mẹo phổ biến để chạy hai bộ -condition/ -actionin độc lập find.

Nếu bạn muốn chạy -action1trên cuộc họp tệp -condition1và độc lập -action2với cuộc họp tệp -condition2, bạn không thể thực hiện:

find . -condition1 -action1 -condition2 -action2

Như -action2sẽ chỉ được chạy cho các tập tin đáp ứng cả hai điều kiện.

Cũng không:

find . -contition1 -action1 -o -condition2 -action2

Như -action2sẽ không được chạy cho các tập tin đáp ứng cả hai điều kiện.

find . \( ! -condition1 -o -action1 \) -condition2 -action2

hoạt động như \( ! -condition1 -o -action1 \)sẽ giải quyết đúng cho mọi tập tin. Giả định đó -action1là một hành động (như -prune, -exec ... {} +) luôn trả về đúng . Đối với những hành động như -exec ... \;có thể trả lại sai , bạn có thể muốn thêm một -o -somethingnơi -somethinglà vô hại nhưng trả về đúng như -truetrong GNU findhay -links +0hay -name '*'(mặc dù lưu ý các vấn đề về nhân vật không hợp lệ ở trên).


1
Một ngày nào đó tôi sẽ chạy vào một loạt các tập tin tiếng Trung và tôi rất vui vì tôi đã đọc nhiều câu trả lời của bạn về ngôn ngữ và ký tự hợp lệ. :)
tự đại diện

2
@Wildcard, bạn (và thậm chí nhiều hơn một người Trung Quốc) có nhiều khả năng gặp vấn đề với tên tệp tiếng Anh, tiếng Pháp hơn tên tệp tiếng Trung vì tên tệp tiếng Trung thường được mã hóa bằng UTF-8 hơn tên tệp của tập lệnh chữ cái mà thường có thể được bao phủ bởi một bộ ký tự byte đơn, là tiêu chuẩn cho đến gần đây. Có các bộ ký tự nhiều byte khác để bao gồm ký tự Trung Quốc, nhưng tôi hy vọng người Trung Quốc sẽ chuyển sang UTF-8 sớm hơn người phương Tây vì các bộ ký tự đó có một số vấn đề khó chịu. Xem thêm chỉnh sửa cho một ví dụ.
Stéphane Chazelas

0

Tôi gặp phải một vấn đề trong đó tôi cần một cách để giới hạn độ sâu khi tìm kiếm nhiều đường dẫn (thay vì chỉ .).

Ví dụ:

$ find dir1 dir2 -name myfile -maxdepth 1

Điều này dẫn tôi đến một cách tiếp cận khác bằng cách sử dụng -regex. Ý chính là:

-regex '(<list of paths | delimited>)/<filename>'

Vì vậy, ở trên sẽ là:

$ find dir1 dir2 -name myfile -regextype awk -regex '(dir1|dir2)/myfile' # GNU
$ find -E dir1 dir2 -name myfile -regex '(dir1|dir2)/myfile' # MacOS BSD

Không có tên tệp:

$ find dir1 dir2 -name myfile -maxdepth 1 # GNU

-regex '(<list of paths | delimited>)/<anything that's not a slash>$'

$ find dir1 dir2 -name myfile -regextype awk -regex '(dir1|dir2)/[^/]*$' # GNU
$ find -E dir1 dir2 -name myfile -regex '(dir1|dir2)/[^/]*$' # MacOS BSD

Cuối cùng, đối với biểu thức chính -maxdepth 2quy thay đổi thành:'(dir1|dir2)/([^/]*/){0,1}[^/]*$'


1
Câu hỏi này yêu cầu một giải pháp tiêu chuẩn (như trong POSIX). Cũng -maxdepthsẽ làm việc với nhiều đường dẫn tìm kiếm.
Kusalananda
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.