Mengapa menemukan terkadang cocok dengan argumen jalur perintahnya?

9

Di Linux,

cd /tmp
mkdir foo; cd foo

Sekarang, berjalan

find . -name 'foo'

tidak memberikan hasil. Padahal sedang berjalan

find /tmp/foo -name 'foo'

Memberikan hasil /tmp/fooyang tidak masuk akal bagi saya. Adakah yang bisa menjelaskan mengapa?

York
sumber
2
temukan celah di dalam jalur yang diberikan termasuk seperti yang dinyatakan. Jadi, jika Anda memasukkannya ./tidak cocokfoo
Costas
@ Costas: Saya mengerti itu. Apa yang saya tidak mengerti adalah mengapa itu membuat perbedaan jika saya memberikan jalan absolut untuk itu find.
York
@York Dalam situasi tertentu tidak ada perilaku yang selalu benar. Bayangkan symlink dengan nama baryang mengarah ke file fooyang berada di luar jalur pencarian. Haruskah itu cocok atau tidak?
Hauke ​​Laging
1
The .dan /tmp/footidak sama - keduanya adalah dua tautan keras berbeda ke direktori yang sama; find /tmp/foo/. -name 'foo'juga tidak menemukan apa pun.
jimmij
@jimmij: ketika saya menjalankan find /tmp/foo -name 'foo', saya meminta bash untuk menemukan di direktori /tmp/foo, file yang namanya "foo". Karena direktori /tmp/footersebut kosong, seharusnya tidak mengembalikan apa pun. Saya tidak mengerti mengapa ia kembali /tmp/foo. Di sisi lain, ketika saya menjalankan find . -name 'foo', saya meminta bash hal yang sama, yaitu, menemukan file di direktori saat ini (yang kebetulan /tmp/foo), yang namanya 'foo', dan tidak mengembalikan apa pun yang masuk akal.
York

Jawaban:

15

findmelintasi pohon direktori yang ditentukan, dan mengevaluasi ekspresi yang diberikan untuk setiap file yang ditemukannya. Traversal dimulai pada jalur yang diberikan. Berikut ringkasan cara find . -name foopengoperasian:

  • Jalur pertama pada baris perintah: .
    • Apakah nama dasar ( .) cocok dengan polanya foo? Tidak, jadi jangan lakukan apa-apa.
      Kebetulan itu /tmp/fooadalah nama lain untuk direktori yang sama. Tapi findtidak tahu itu (dan tidak seharusnya mencoba mencari tahu).
    • Apakah path direktori? Ya, jadi lewati saja. Hitung entri ., dan untuk setiap entri, lakukan proses traversal.
      • Direktori kosong: tidak berisi entri selain .dan .., yang findtidak melintasi secara rekursif. Jadi pekerjaan selesai.

Dan find /tmp/foo:

  • Jalur pertama pada baris perintah: /tmp/foo
    • Apakah nama dasar ( foo) cocok dengan polanya foo? Ya, jadi kondisinya cocok.
      • Tidak ada tindakan yang terkait dengan kondisi ini, jadi lakukan tindakan default, yaitu mencetak path.
    • Apakah path direktori? Ya, jadi lewati saja. Hitung entri /tmp/foo, dan untuk setiap entri, lakukan proses traversal.
      • Direktori kosong: tidak berisi entri selain .dan .., yang findtidak melintasi secara rekursif. Jadi pekerjaan selesai.

Kebetulan itu .dan /tmp/foodirektori yang sama, tetapi itu tidak cukup untuk menjamin bahwa findmemiliki perilaku yang sama pada keduanya. The findperintah memiliki cara untuk membedakan antara jalur ke file yang sama; yang -namepredikat adalah salah satunya. find /tmp/foo -name foococok dengan direktori awal serta file apa pun di bawahnya yang dipanggil foo. find . -name .hanya cocok dengan direktori awal ( .tidak pernah dapat ditemukan selama traversal rekursif).

Gilles 'SANGAT berhenti menjadi jahat'
sumber
"tidak mengandung entri selain dan dan, yang ditemukan tidak melintasi secara rekursif." Saya berasumsi "tidak melintang secara rekursif" merujuk ke "..". Jika itu benar, lalu apa yang akan "transversal secara rekursif" artinya dalam konteks ".."?
Faheem Mitha
@FaheemMitha “tidak melintasi secara rekursif” berlaku untuk keduanya .dan ... (Jika finddilalui .secara berulang, itu akan berakhir melalui direktori lagi dan lagi dan lagi dan ...) .dan ..bukan hanya notasi khusus, mereka muncul sebagai entri direktori.
Gilles 'SANGAT berhenti menjadi jahat'
5

Tidak ada normalisasi argumen baris perintah sebelum tes diterapkan. Dengan demikian hasilnya berbeda tergantung pada jalur yang digunakan (jika symlinks terlibat):

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

Dalam "kasus Anda" kedua panggilan akan memberikan hasil yang sama yang mungkin (lebih) membingungkan. Anda dapat menggunakan -mindepth 1jika Anda ingin titik awal diabaikan (mungkin non-POSIX).

Hauke ​​Laging
sumber
-mindepth 1bekerja. Namun, saya masih tidak mengerti mengapa find . -name 'foo'dan find /tmp/foo -name 'foo'berperilaku berbeda.
York
2

(gnu) find menunjukkan kecocokan apa pun yang ditemukan di dalam jalur yang disediakan untuk perintah karena ia memulai perbandingannya dengan argumen commandline, turun lebih dalam ke struktur direktori dari sana (dengan demikian, -maxdepth 0membatasi pengujian hanya pada level dasar atau argumen commandline saja, sedangkan -mindepth 1melewatkan argumen baris perintah seperti yang man finddijelaskan). Ini adalah alasan mengapa find /tmp/foo -name 'foo'akan menghasilkan satu kecocokan bahkan jika direktori itu sendiri kosong.

find . -name 'foo'di sisi lain tidak akan menghasilkan hasil apa pun karena .(dot) adalah file khusus yang bertindak seperti hardlink ke inode yang sama /tmp/foo- itu seperti nama file yang terpisah (meskipun khusus) dan bukan tautan simbolis atau ekspresi yang dikenakan ekspansi pathname oleh shell. Oleh karena itu, tes pertama yang diterapkan oleh find ke argumen commandline dalam contoh yang diberikan tidak akan menunjukkan kecocokan, karena .memang tidak cocok dengan pola nama yang didefinisikan dalam -name 'foo'. Tidak juga /tmp/foo/.karena tes untuk suatu -namepola dilakukan hanya pada nama dasar jalan (lihat man find), yang di sini lagi adalah ..

Sementara perilaku ini mungkin tidak diharapkan atau muncul intuitif dari perspektif pengguna (dan ya, itu membuat saya bingung juga pada awalnya), itu bukan merupakan bug tetapi sesuai dengan logika dan fungsionalitas yang dijelaskan dalam man dan halaman info untuk ( gnu) temukan.

Shevek
sumber
0

Saya sudah mencoba mengomentari jawaban Gilles, tetapi terlalu lama untuk memuat komentar. Untuk alasan ini, saya meletakkannya sebagai jawaban untuk pertanyaan saya sendiri.

Penjelasan Gilles (dan juga Shevek) jelas dan masuk akal. Titik kunci di sini adalah bahwa tidak hanya findmencoba mencocokkan nama file untuk file di dalam jalur yang diberikan (secara rekursif), tetapi juga, ia mencoba untuk mencocokkan nama dasar dari jalur yang diberikan sendiri.

Di sisi lain, apakah ada bukti bahwa ini adalah cara yang findseharusnya berfungsi, daripada menjadi bug? Menurut pendapat saya, akan lebih baik jika tidak membuat hasil tidak konsisten untuk find .dan find ABSOLUTE-PATHkarena ketidakkonsistenan selalu membingungkan dan dapat menyia-nyiakan pengembang banyak waktu mencoba untuk mencari tahu "apa yang salah". Dalam kasus saya, saya sedang menulis skrip, dan path diambil dari variabel. Jadi agar skrip saya berfungsi dengan baik, yang bisa saya pikirkan adalah menulis find $path/. -name 'pattern'.

Akhirnya, saya pikir mendapatkan hasil yang konsisten untuk finddapat dicapai dengan selalu mengganti .dengan direktori saat ini sebelum melanjutkan.

York
sumber
-1

Tidak ada objek yang disebutkan foodalam direktori tempat Anda memulai pencarian relatif.

Anda benar dengan menganggap bahwa gfindbermasalah ketika melaporkan /tmp/fooketika menggunakan nama absolut sebagai direktori awal.

Gfindmemiliki berbagai penyimpangan dari standar, sepertinya Anda menemukan yang lain. Ketika Anda menyukai solusi yang lebih standar, saya sarankan sfinditu adalah bagian dari schilytools.

-nameberlaku untuk hasil pencarian direktori. Tidak satu pun dari dua kasus yang Anda sebutkan akan mengembalikan entri direktori foodari readdir()operasi.

schily
sumber
Saya menduga ini juga bug. Berikut ini keluaran dari find --version: find (GNU findutils) 4.4.2 Hak Cipta (C) 2007 Free Software Foundation, Inc. Lisensi GPLv3 +: GNU GPL versi 3 atau lebih baru < gnu.org/licenses/gpl.html >
York
Yah, saya memeriksa perintah contoh Anda dengan find(POSIX bersertifikat), gfinddan sfind; masalahnya hanya ada saat Anda menggunakan gfind. Di Linux, gfindtidak diinstal di bawah nama asli tetapi di bawah nama find.
schily
3
Benar untuk find /tmp/foo -name foooutput /tmp/foo. (Cc @York) Ini adalah perilaku OpenBSD find, GNU find, BusyBox find, Solaris find, dan semua yang sesuai dengan POSIX lainnya find(“Primer harus dievaluasi sebagai benar jika nama file dari nama file yang diperiksa cocok dengan pola (…)” - ketika nama file adalah salah satu yang disahkan sebagai operan jalur, tidak ada readdirpanggilan yang terlibat).
Gilles 'SANGAT berhenti menjadi jahat'