Bagaimana cara menggunakan opsi '-prune' dari 'find' di sh?

219

Saya tidak begitu mengerti contoh yang diberikan dari man find, bisakah ada yang memberi saya beberapa contoh dan penjelasan? Bisakah saya menggabungkan ekspresi reguler di dalamnya?


Pertanyaan yang lebih rinci adalah seperti ini:

Tulis skrip shell changeall,, yang memiliki antarmuka seperti changeall [-r|-R] "string1" "string2". Ini akan menemukan semua file dengan akhiran .h, .C, .cc, atau .cppdan mengubah semua kejadian string1untuk string2. -radalah pilihan untuk tinggal di dir saja saat ini atau termasuk subdir's.

CATATAN:

  1. Untuk kasus non-rekursif, lsTIDAK diizinkan, kami hanya bisa menggunakan finddan sed.
  2. Saya mencoba find -depthtetapi TIDAK didukung. Itu sebabnya saya bertanya-tanya apakah -prunebisa membantu, tetapi tidak mengerti contoh dari man find.

EDIT2: Saya sedang melakukan tugas, saya tidak mengajukan pertanyaan dengan sangat rinci karena saya ingin menyelesaikannya sendiri. Karena saya sudah melakukannya dan menyerahkannya, sekarang saya dapat menyatakan seluruh pertanyaan. Juga, saya berhasil menyelesaikan tugas tanpa menggunakan -prune, tetapi tetap ingin mempelajarinya.

derrdji
sumber

Jawaban:

438

Hal yang saya temukan membingungkan -pruneadalah bahwa itu adalah tindakan (seperti -print), bukan ujian (seperti -name). Itu mengubah daftar "yang harus dilakukan", tetapi selalu mengembalikan true .

Pola umum untuk menggunakan -pruneadalah ini:

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

Anda hampir selalu menginginkan -o(logis ATAU) segera setelah itu -prune, karena bagian pertama dari tes (hingga dan termasuk -prune) akan mengembalikan false untuk hal-hal yang sebenarnya Anda inginkan (yaitu: hal-hal yang Anda tidak ingin pangkas).

Ini sebuah contoh:

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

Ini akan menemukan file "* .foo" yang tidak di bawah direktori ".snapshot". Dalam contoh ini, -name .snapshotmake up [conditions to prune], and -name '*.foo' -printis [your usual conditions]dan [actions to perform].

Catatan penting :

  1. Jika semua yang ingin Anda lakukan adalah mencetak hasil yang mungkin Anda gunakan untuk meninggalkan -printtindakan. Anda biasanya tidak ingin melakukannya saat menggunakan -prune.

    Perilaku default find adalah "dan" seluruh ekspresi dengan -printaksi jika tidak ada tindakan selain -prune(ironisnya) di akhir. Itu artinya menulis ini:

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

    sama dengan menulis ini:

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

    yang berarti itu juga akan mencetak nama direktori yang Anda pangkas, yang biasanya bukan yang Anda inginkan. Alih-alih, lebih baik menentukan -printtindakan secara eksplisit jika itu yang Anda inginkan:

    find . -name .snapshot -prune -o -name '*.foo' -print       # DO THIS
  2. Jika "kondisi biasa" Anda cocok dengan file yang juga cocok dengan kondisi prune Anda, file-file itu tidak akan dimasukkan dalam output. Cara untuk memperbaikinya adalah dengan menambahkan-type d predikat ke kondisi prune Anda.

    Sebagai contoh, misalkan kita ingin memangkas direktori yang dimulai dengan .git(ini diakui agak dibuat - biasanya Anda hanya perlu menghapus hal bernama persis .git ), tetapi selain itu ingin melihat semua file, termasuk file seperti .gitignore. Anda dapat mencoba ini:

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

    Ini tidak akan termasuk .gitignoredalam output. Ini versi yang sudah diperbaiki:

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

Kiat tambahan: jika Anda menggunakan versi GNU find, halaman texinfo untuk findmemiliki penjelasan yang lebih rinci daripada halaman manuvernya (sebagaimana berlaku untuk sebagian besar utilitas GNU).

Laurence Gonsalves
sumber
6
itu tidak 100% jelas dalam teks Anda (tetapi karena Anda hanya mencetak '* .foo' itu tidak bertentangan) tetapi bagian -prune juga tidak akan mencetak apa pun (tidak hanya direktori) bernama ".snapshot". yaitu, -prunetidak hanya bekerja pada direktori (tetapi, untuk direktori, itu juga mencegah memasuki direktori yang cocok dengan kondisi itu, yaitu di sini dirs yang cocok dengan itu -name .snapshot).
Olivier Dulac
12
dan +1 untuk Anda untuk penjelasan yang dilakukan dengan baik (dan terutama catatan penting). Anda harus mengirimkan ini ke find developpers (karena halaman manual tidak menjelaskan "pangkas" untuk manusia normal ^^ Butuh banyak percobaan untuk mencari tahu, dan saya tidak melihat efek samping yang Anda peringatkan pada kami)
Olivier Dulac
2
@OlivierDulac Itu poin yang sangat bagus tentang berpotensi menghapus file yang ingin Anda simpan. Saya telah memperbarui jawaban untuk memperjelas ini. -pruneOmong-omong, sebenarnya bukan itu yang menyebabkan ini. Masalahnya adalah bahwa operator atau "korsleting", dan atau memiliki prioritas lebih rendah daripada dan. Hasil akhirnya adalah bahwa jika file yang dipanggil .snapshotditemukan akan cocok dengan yang pertama -name, -prunemaka tidak akan melakukan apa-apa (tapi mengembalikan true), dan kemudian atau mengembalikan true karena argumen kiri itu benar. Tindakan (misalnya:) -printadalah bagian dari argumen kedua, sehingga tidak pernah memiliki kesempatan untuk mengeksekusi.
Laurence Gonsalves
3
+1 akhirnya menemukan mengapa saya perlu -printdi akhir, sekarang saya dapat berhenti menambahkan \! -path <pattern>di samping-prune
Variabel
6
Perhatikan bahwa "-o" adalah kependekan dari "-atau", yang (walaupun tidak sesuai dengan POSIX) berbunyi lebih jelas.
yoyo
27

Biasanya cara asli kita melakukan hal-hal di linux dan cara kita berpikir dari kiri ke kanan.
Jadi, Anda akan pergi dan menulis apa yang Anda cari terlebih dahulu:

find / -name "*.php"

Maka Anda mungkin menekan enter dan menyadari Anda mendapatkan terlalu banyak file dari direktori yang tidak Anda inginkan. Mari kecualikan / media untuk menghindari pencarian drive yang Anda pasang.
Sekarang Anda hanya harus LAMPIRAN berikut ke perintah sebelumnya:

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

jadi perintah terakhir adalah:

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

............... | <--- Sertakan ---> | .................... | <- -------- Kecualikan ---------> |

Saya pikir struktur ini jauh lebih mudah dan berkorelasi dengan pendekatan yang tepat

AmitP
sumber
3
Saya tidak akan mengharapkan ini menjadi efisien - saya akan berpikir bahwa itu akan mengevaluasi klausa kiri terlebih dahulu sebelum memangkas, tetapi saya terkejut tes cepat tampaknya menunjukkan bahwa findcukup pintar untuk memproses -pruneklausa terlebih dahulu. Hmmm, menarik.
artfulrobot
Saya tidak pernah mempertimbangkan bahwa dalam hampir satu dekade menggunakan GNU find! Terima kasih untuk itu! Ini pasti akan mengubah cara saya berpikir -prunemulai sekarang.
Felipe Alvarez
3
@artfulrobot Apakah ini benar-benar memprosesnya terlebih dahulu? Saya akan berpikir itu masuk /media, memperhatikan bahwa itu tidak dipanggil *.phpdan kemudian memeriksa apakah itu saat ini di dalam /media, melihat bahwa itu dan karena itu melewatkan seluruh subtree. Itu masih dari kiri ke kanan, itu tidak ada bedanya selama kedua cek tidak tumpang tindih.
phk
26

Waspadalah bahwa -prune tidak mencegah turun ke direktori apa pun seperti yang dikatakan beberapa orang. Itu mencegah turun ke direktori yang cocok dengan tes itu diterapkan. Mungkin beberapa contoh akan membantu (lihat bagian bawah untuk contoh regex). Maaf karena ini sangat panjang.

$ 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
Dijeda sampai pemberitahuan lebih lanjut.
sumber
jika Anda juga "menyentuh ./dir1/scripts/test" (yaitu, memiliki file "test", dan bukan dir, dalam subdir yang dicetak), ia tidak akan mendapatkan printd oleh find . -name test -prune -o -print: iow, -pruneadalah tindakan yang juga bekerja pada file
Olivier Dulac
10

Menambahkan saran yang diberikan dalam jawaban lain (saya tidak punya perwakilan untuk membuat balasan) ...

Saat menggabungkan -prunedengan ekspresi lain, ada perbedaan perilaku yang halus tergantung pada ekspresi lain yang digunakan.

Contoh @Laurence Gonsalves 'akan menemukan file "* .foo" yang tidak di bawah direktori ".snapshot": -

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

Namun, tangan pendek yang sedikit berbeda ini akan, mungkin secara tidak sengaja, juga mencantumkan .snapshotdirektori (dan direktori .snapshot bersarang): -

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

Alasannya adalah (menurut halaman manual pada sistem saya): -

Jika ungkapan yang diberikan tidak mengandung salah satu dari primary -exec, -l, -ok, atau -print, ekspresi yang diberikan secara efektif diganti oleh:

(given_expression) -print

Yaitu, contoh kedua adalah setara dengan memasukkan yang berikut, dengan demikian memodifikasi pengelompokan istilah: -

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

Ini setidaknya terlihat pada Solaris 5.10. Setelah menggunakan berbagai rasa * nix selama sekitar 10 tahun, saya baru saja mencari alasan mengapa ini terjadi.

crw
sumber
Terima kasih telah memperhatikan perbedaan antara menggunakan -prunedengan dan tanpa -print!
mcw
3

Prune adalah jangan berulang di setiap saklar direktori.

Dari halaman manual

Jika -depth tidak diberikan, benar; jika file tersebut adalah direktori, jangan turun ke dalamnya. Jika -depth diberikan, false; tidak berpengaruh.

Pada dasarnya itu tidak akan masuk ke sub direktori.

Ambil contoh ini:

Anda memiliki direktori berikut

  • / home / test2
  • / home / test2 / test2

Jika Anda menjalankan find -name test2:

Ini akan mengembalikan kedua direktori

Jika Anda menjalankan find -name test2 -prune:

Hanya akan mengembalikan / home / test2 karena tidak akan turun ke / home / test2 untuk menemukan / home / test2 / test2

AdamW
sumber
tidak 100% benar: ini adalah "lakukan pemangkasan saat mencocokkan kondisi, dan jika itu adalah direktori, keluarkan dari daftar yang harus dilakukan, yaitu jangan masukkan juga". -prune juga berfungsi pada file.
Olivier Dulac
2

Saya bukan ahli dalam hal ini (dan halaman ini sangat membantu bersama dengan http://mywiki.wooledge.org/UsingFind )

Hanya memperhatikan -pathadalah untuk jalur yang sepenuhnya cocok dengan string / jalur yang datang tepat setelahfind ( .dalam contoh tesis) di mana -namecocok dengan semua nama dasar.

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

memblokir direktori .git di direktori Anda saat ini ( seperti temuan Anda . )

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

blok semua subdirektori .git secara rekursif.

Perhatikan ./ ini sangat penting !! -pathharus cocok dengan jalur yang dilabuhkan ke . atau apa pun yang datang setelah menemukan jika Anda mendapatkan kecocokan dengan itu (dari sisi lain atau ' -o') mungkin tidak ada pemangkasan! Saya naif tidak menyadari hal ini dan itu membuat saya menggunakan -path ketika itu bagus ketika Anda tidak ingin memangkas semua subdirektori dengan nama yang sama: D

sabgenton
sumber
Catat jika find bla/bla/*
ulah
1

Tampilkan semuanya termasuk dir itu sendiri tetapi tidak isinya membosankan:

find . -print -name dir -prune
setan
sumber
0

Jika Anda membaca semua jawaban yang baik di sini, pemahaman saya sekarang adalah bahwa semua jawaban berikut memberikan hasil yang sama:

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

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

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

Tapi yang terakhir akan memakan waktu lebih lama karena masih mencari semuanya di dir1. Saya kira pertanyaan sebenarnya adalah bagaimana caranya-or mengeluarkan hasil yang tidak diinginkan tanpa benar-benar mencari mereka.

Jadi saya kira prune berarti tidak cocok dengan pertandingan sebelumnya tetapi menandainya sudah selesai ...

http://www.gnu.org/software/findutils/manual/html_mono/find.html "Namun ini bukan karena efek tindakan '-prune' (yang hanya mencegah penurunan lebih lanjut, itu tidak memastikan kita mengabaikan item itu). Alih-alih, efek ini adalah karena penggunaan '-o'. Karena sisi kiri dari kondisi "atau" telah berhasil untuk ./src/emacs, tidak perlu untuk mengevaluasi hak- sisi-tangan ('-cetak') sama sekali untuk file khusus ini. "

sabgenton
sumber
0

findmembangun daftar file. Ini menerapkan predikat yang Anda berikan untuk masing-masing dan mengembalikan yang lulus.

Gagasan ini yang -pruneberarti mengecualikan dari hasil benar-benar membingungkan bagi saya. Anda dapat mengecualikan file tanpa pangkas:

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

Yang -prunedilakukan hanyalah mengubah perilaku pencarian. Jika kecocokan saat ini adalah direktori, dikatakan "hei find, file yang baru saja Anda cocokkan, jangan turun ke dalamnya" . Itu hanya menghapus pohon itu (tetapi bukan file itu sendiri) dari daftar file yang akan dicari.

Itu harus dinamai -dont-descend.

seeker_of_bacon
sumber
0

Ada beberapa jawaban; beberapa dari mereka terlalu banyak teori-berat. Saya akan meninggalkan mengapa saya perlu memangkas sekali jadi mungkin perlu-pertama / contoh penjelasan berguna bagi seseorang :)

Masalah

Saya punya folder dengan sekitar 20 direktori simpul, masing-masing punya node_modules direktori seperti yang diharapkan.

Setelah Anda masuk ke proyek apa pun, Anda melihat masing-masing ../node_modules/module. Tapi Anda tahu bagaimana itu. Hampir setiap modul memiliki dependensi, jadi apa yang Anda lihat lebih miripprojectN/node_modules/moduleX/node_modules/moduleZ...

Saya tidak ingin tenggelam dengan daftar dengan ketergantungan dari ketergantungan ...

Mengetahui -d n/ -depth n, itu tidak akan membantu saya, karena direktori node_modules utama / pertama yang saya inginkan dari setiap proyek berada pada kedalaman yang berbeda, seperti ini:

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

Bagaimana saya bisa mendapatkan yang pertama daftar jalan berakhir pada yang pertama node_modulesdan pindah ke proyek berikutnya untuk mendapatkan yang sama?

Memasukkan -prune

Saat Anda menambahkan -prune, Anda akan tetap memiliki pencarian rekursif standar. Setiap "jalan" dianalisis, dan setiap temuan akan dimuntahkan dan findterus menggali seperti orang yang baik. Tapi ini menggali lebih dalam untuk node_modulesapa yang tidak saya inginkan.

Jadi, perbedaannya adalah bahwa di setiap jalur yang berbeda itu, -pruneakan findberhenti menggali lebih jauh ke jalan tertentu ketika telah menemukan item Anda. Dalam kasus saya, node_modulesfolder.

Carles Alcolea
sumber