Tại sao find đôi khi khớp với đối số đường dẫn dòng lệnh của nó?


9

Trên Linux,

cd /tmp
mkdir foo; cd foo

Bây giờ, đang chạy

find . -name 'foo'

không cho đầu ra. Trong khi chạy

find /tmp/foo -name 'foo'

Cung cấp đầu ra /tmp/fookhông có ý nghĩa với tôi. Ai đó có thể giải thích tại sao?


2
tìm seaches bên trong con đường nhất định bao gồm như nó được nêu. Vì vậy, nếu bạn nhập ./nó không khớpfoo
Costas

@Costas: Tôi hiểu điều đó. Điều tôi không hiểu là tại sao nó lại tạo ra sự khác biệt nếu tôi đưa ra con đường tuyệt đối find.
York

@York Trong một số tình huống nhất định không có hành vi nào luôn đúng. Hãy tưởng tượng một liên kết tượng trưng với tên bartrỏ đến một tệp foonằm ngoài đường dẫn tìm kiếm. Có phù hợp hay không?
Hauke ​​Laging

1
Các ./tmp/fookhông giống nhau - họ là hai liên kết cứng khác nhau để cùng thư mục; find /tmp/foo/. -name 'foo'cũng không tìm thấy gì cả.
jimmij

@jimmij: khi tôi chạy find /tmp/foo -name 'foo', tôi đã yêu cầu bash tìm trong thư mục /tmp/foo, một tệp có tên là "foo". Bởi vì thư mục /tmp/footrống, nên nó không trả về gì cả. Tôi không hiểu tại sao nó trở lại /tmp/foo. Mặt khác, khi tôi chạy find . -name 'foo', tôi đã hỏi bash điều tương tự, tức là tìm một tệp trong thư mục hiện tại (có thể là /tmp/foo), tên của nó là 'foo', và nó không trả về bất cứ điều gì hợp lý.
York

Câu trả lời:


15

findduyệt qua (các) cây thư mục được chỉ định và đánh giá biểu thức đã cho cho mỗi tệp mà nó tìm thấy. Các đường ngang bắt đầu tại đường dẫn đã cho. Dưới đây là tóm tắt về cách find . -name foovận hành:

  • Đường dẫn đầu tiên trên dòng lệnh: .
    • Tên cơ sở ( .) có khớp với mẫu fookhông? Không, không làm gì cả.
      /tmp/foolà một tên khác cho cùng một thư mục. Nhưng findkhông biết điều đó (và không cần phải cố gắng tìm hiểu).
    • Là đường dẫn một thư mục? Vâng, vì vậy đi qua nó. Liệt kê các mục trong .và cho mỗi mục, thực hiện quy trình truyền tải.
      • Thư mục rỗng: nó không chứa entry khác hơn ..., mà findkhông đi qua một cách đệ quy. Vậy là xong việc.

find /tmp/foo:

  • Đường dẫn đầu tiên trên dòng lệnh: /tmp/foo
    • Tên cơ sở ( foo) có khớp với mẫu fookhông? Có, vì vậy điều kiện phù hợp.
      • Không có hành động liên quan đến điều kiện này, vì vậy hãy thực hiện hành động mặc định, đó là in đường dẫn.
    • Là đường dẫn một thư mục? Vâng, vì vậy đi qua nó. Liệt kê các mục trong /tmp/foovà cho mỗi mục, thực hiện quy trình truyền tải.
      • Thư mục rỗng: nó không chứa entry khác hơn ..., mà findkhông đi qua một cách đệ quy. Vậy là xong việc.

Điều đó xảy ra ./tmp/foolà cùng một thư mục, nhưng điều đó không đủ để đảm bảo findcó cùng một hành vi trên cả hai. Các findlệnh có cách để phân biệt giữa đường dẫn đến tập tin tương tự; các -namevị ngữ là một trong số họ. find /tmp/foo -name foophù hợp với thư mục bắt đầu cũng như bất kỳ tập tin nào bên dưới nó được gọi foo. find . -name .chỉ khớp với thư mục bắt đầu ( .không bao giờ có thể được tìm thấy trong một lần truy cập đệ quy).


"nó không chứa mục nào khác ngoài. và .., tìm thấy không đi qua đệ quy." Tôi giả sử "không chuyển tiếp đệ quy" đề cập đến "..". Nếu điều đó là chính xác, thì "đệ quy ngang" nghĩa là gì trong ngữ cảnh của ".."?
Faheem Mitha

@FaheemMitha không vượt qua đệ quy áp dụng cho cả hai .... (Nếu findđi qua .đệ quy, nó sẽ đi qua thư mục hết lần này đến lần khác và một lần nữa) ...không chỉ là các ký hiệu đặc biệt, chúng xuất hiện dưới dạng các mục trong thư mục.
Gilles 'SO- ngừng trở nên xấu xa'

5

Không có sự chuẩn hóa của các đối số dòng lệnh trước khi các bài kiểm tra được áp dụng. Do đó, kết quả khác nhau tùy thuộc vào đường dẫn được sử dụng (nếu có liên kết tượng trưng):

cd /tmp
mkdir foo
ln -s foo bar
find /tmp/foo -name foo
find /tmp/bar -name foo

Trong "trường hợp của bạn" cả hai cuộc gọi sẽ cho cùng một kết quả có thể gây nhầm lẫn (nhiều hơn). Bạn có thể sử dụng -mindepth 1nếu bạn muốn các điểm bắt đầu bị bỏ qua (có thể không phải là POSIX).


-mindepth 1làm. Tuy nhiên, tôi vẫn không hiểu tại sao find . -name 'foo'find /tmp/foo -name 'foo'cư xử khác.
York

2

(gnu) find hiển thị bất kỳ kết quả khớp nào được tìm thấy trong đường dẫn được cung cấp cho lệnh vì nó bắt đầu so sánh với các đối số dòng lệnh, đi sâu hơn vào cấu trúc thư mục từ đó (do đó, chỉ -maxdepth 0giới hạn các kiểm tra ở mức cơ sở hoặc chỉ đối số dòng lệnh -mindepth 1bỏ qua các đối số dòng lệnh như man findgiải thích). Đây là lý do tại sao find /tmp/foo -name 'foo'sẽ mang lại một trận đấu ngay cả khi chính thư mục trống.

find . -name 'foo'mặt khác sẽ không mang lại bất kỳ kết quả nào vì .(dấu chấm) là một tệp đặc biệt hoạt động giống như một liên kết cứng đến cùng một nút như /tmp/foo- nó giống như một tên tệp riêng biệt (mặc dù đặc biệt) và không phải là một liên kết tượng trưng hoặc một biểu thức phải chịu mở rộng tên đường dẫn bằng vỏ. Do đó, thử nghiệm đầu tiên được áp dụng bởi find cho các đối số dòng lệnh trong ví dụ đã cho sẽ không hiển thị bất kỳ kết quả khớp nào, vì .thực sự không khớp với mẫu tên được xác định trong -name 'foo'. Không phải /tmp/foo/.vì một bài kiểm tra cho một -namemẫu được thực hiện trên tên cơ sở của đường dẫn (xem man find), mà ở đây một lần nữa là ..

Mặc dù hành vi này có thể không được mong đợi hoặc xuất hiện trực quan từ góc độ người dùng (và vâng, lúc đầu tôi cũng bối rối), nó không tạo thành lỗi nhưng tương ứng với logic và chức năng được mô tả trong trang người đàn ông và thông tin cho ( gnu) tìm.


0

Tôi đã thử bình luận câu trả lời của Gilles, nhưng nó quá dài để phù hợp với một bình luận. Vì lý do này, tôi đặt nó như một câu trả lời cho câu hỏi của riêng tôi.

Lời giải thích của Gilles (và Shevek cũng vậy) rất rõ ràng và có ý nghĩa. Điểm mấu chốt ở đây là không chỉ findcố gắng khớp tên tệp cho các tệp bên trong các đường dẫn đã cho (theo cách đệ quy) mà còn cố gắng khớp chính tên cơ sở của các đường dẫn đã cho.

Mặt khác, có bằng chứng nào cho thấy đây là cách findgiả sử để hoạt động, thay vì là một lỗi không? Theo tôi, sẽ tốt hơn nếu nó không làm cho kết quả không nhất quán find .find ABSOLUTE-PATHbởi vì sự không nhất quán luôn gây nhầm lẫn và có thể lãng phí rất nhiều thời gian của nhà phát triển khi cố gắng tìm ra "điều gì sai". Trong trường hợp của tôi, tôi đã viết một kịch bản và đường dẫn được lấy từ một biến. Vì vậy, để kịch bản của tôi hoạt động đúng, điều tôi có thể nghĩ là làm là viết find $path/. -name 'pattern'.

Cuối cùng, tôi nghĩ rằng việc có được kết quả nhất quán findcó thể đạt được bằng cách luôn luôn thay thế .thư mục hiện tại trước khi tiếp tục.


-1

Không có đối tượng có tên footrong thư mục nơi bạn bắt đầu tìm kiếm tương đối.

Bạn đã đúng khi cho rằng đó gfindlà lỗi khi nó báo cáo /tmp/fookhi sử dụng tên tuyệt đối làm thư mục bắt đầu.

Gfindcó nhiều sai lệch so với tiêu chuẩn, có vẻ như bạn đã tìm thấy một số khác. Khi bạn thích một giải pháp tuân thủ tiêu chuẩn hơn, tôi khuyên bạn nên sfindlà một phần của schilytools.

-nameáp dụng cho kết quả tìm kiếm thư mục. Không có trường hợp nào trong hai trường hợp bạn đề cập sẽ trả về một mục nhập thư mục footừ một readdir()hoạt động.


Tôi nghi ngờ đây là một lỗi quá. Đây là đầu ra từ find --version: find (GNU findutils) 4.4.2 Bản quyền (C) 2007 Free Software Foundation, Inc. Giấy phép GPLv3 +: GNU GPL phiên bản 3 trở lên < gnu.org/licenses/gpl.html >
York

Chà, tôi đã kiểm tra các lệnh ví dụ của bạn với find(chứng nhận POSIX) gfindsfind; vấn đề chỉ tồn tại khi bạn sử dụng gfind. Trên Linux, gfindkhông được cài đặt dưới tên gốc mà dưới tên find.
schily

3
Nó là chính xác cho find /tmp/foo -name foođầu ra /tmp/foo. (Cc @York) Đây là hành vi của OpenBSD find, GNU find, BusyBox find, Solaris findvà bất kỳ tuân thủ POSIX nào khác find(Tên chính sẽ đánh giá là đúng nếu tên cơ sở của tên tệp được kiểm tra khớp với mẫu (ví dụ) là một trong đó được thông qua như một toán hạng đường dẫn, không có readdircuộc gọi nào được tham gia).
Gilles 'SO- ngừng trở nên xấu xa'
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.