Bagaimana saya menggunakan `find` untuk pergi ke direktori file itu

11

Saya ingin mencari file dan kemudian memasukkan direktori yang berisi itu. Saya mencoba find /media/storage -name "Fedora" | xargs cdtetapi tentu saja, saya is not a directorykesalahannya.

Bagaimana cara memasukkan direktori induknya dengan perintah satu baris?

Hrvoje T
sumber
1
Dan bagaimana jika ada banyak file dari berbagai lokasi?
Sergiy Kolodyazhnyy
@Serg Im mencari file Fedora * .iso dan saya tahu hanya ada satu. Jika ada lebih dari satu yang akan memasuki direcotry pertama, saya kira
Hrvoje T
Dalam bash with shopt -s globstar, Anda bisa cd /media/storage/**/Fedora, tetapi itu tidak berhenti mengevaluasi glob pada pertandingan pertama (jadi itu lebih lambat daripada solusi steeldriver. Untuk penggunaan interaktif, apa yang biasanya saya lakukan adalah meraih mouse dan menyalin / menempelkan nama direktori, (dan alt + backspace yang diperlukan untuk menanggalkan komponen path trailing yang tidak saya inginkan), tetapi jika Anda sering melakukan ini, saya rasa fungsi shell bisa bernilai.
Peter Cordes
1
BTW, xargs cdtidak mungkin bekerja. cdhanya dapat berfungsi sebagai shell builtin, karena harus memodifikasi konteks shell itu sendiri. Tidak mungkin xargsproses anak dapat melakukannya. IDK jika itu yang Anda maksud dengan "tentu saja", atau jika jalur yang findmencetak berisi spasi, yang dipisahkan oleh xargs karena Anda tidak menggunakan -d \natau apa pun. Atau find -exec {} \;.
Peter Cordes
Catatan: Anda tidak bisa lari cdseperti itu. cdadalah sebuah bash yang dibangun, jika cdmerupakan perintah yang terpisah, maka itu akan mengubah dir (miliknya), dan kemudian berhenti (mengembalikan Anda ke shell, yang berada dalam keadaan yang sama seperti sebelumnya, tidak ada perubahan dir).
ctrl-alt-delor

Jawaban:

14

Setidaknya jika Anda memiliki GNU find, Anda dapat menggunakan -printf '%h'untuk mendapatkan direktori

       %h     Leading directories of file's name (all but the last ele‐
              ment).  If the file name contains no slashes (since it is
              in  the  current  directory)  the %h specifier expands to
              ".".

Jadi mungkin Anda bisa melakukannya

cd "$(find /media/storage -name "Fedora" -printf '%h' -quit)"

The -quitseharusnya mencegah beberapa argumen untuk cddalam kasus lebih dari satu file cocok.

Steeldriver
sumber
1
-quitjuga belum tentu didukung. Di NetBSD namanya -exit, lihat unix.stackexchange.com/a/62883/117599
phk
2
Jika tidak ada printf yang bisa Anda lakukan -exname bukan?
Guy
@Beli ide bagus ya itu kedengarannya harus bekerja juga
steeldriver
6

Mirip dengan solusi steeldriver tetapi menggunakan -execdir(jika Anda findmendukungnya, seperti GNU atau FreeBSD find) dalam kombinasi dengan pwd:

cd "$(find /media/storage -name "Fedora" -execdir pwd \; -quit)"

-quitbersifat opsional jika hanya ada satu hasil dan merayapi seluruh direktori tidak ada masalah. Di NetBSD itu -exitdan di OpenBSD tidak ada.

phk
sumber
Dan untuk apa \;?
Hrvoje T
1
@HrvojeT Sama seperti -execitu menceritakan findtentang akhir parameter untuk perintah untuk dieksekusi. Tetapi karena kita ingin memanggil pwdtanpa parameter di sini, kita meletakkannya \;tepat setelahnya.
phk
Apakah ada findimplementasi yang mendukung execdir tetapi tidak -printf %h? Sepertinya tidak bagi saya. Sayangnya tidak ada yang diminta oleh POSIX: /
Peter Cordes
1
@PeterCordes FreeBSD's find: freebsd.org/cgi/man.cgi?find%281%29 (Baru saja mengonfirmasikannya pada instalasi FreeBSD 11).
phk
@PeterCordes Sama untuk NetBSD ( netbsd.gw.com/cgi-bin/man-cgi?find++NetBSD-current ) dan OpenBSD ( man.openbsd.org/OpenBSD-current/man1/find.1 ). Namun belakangan tidak mendukung -quit/ -exitsama sekali.
phk
5

Anda dapat membuat find menjalankan shell baru di direktori yang ditemukannya.

exec find /media/storage -name "Fedora" -execdir "$SHELL" \;

, setelah itu direktori saat ini akan menjadi orang yang memiliki file bernama Fedora di dalamnya. ;)

Jelas ini hanya melakukan sesuatu yang menyerupai apa yang Anda inginkan jika Anda mengetik perintah secara interaktif.

BenGoldberg
sumber
4

Dengan zsh:

cd /media/storage/**/Fedora([1]:h)

untuk cdmasuk ke direktori pertama (dalam urutan abjad) yang berisi file bernama Fedora.

  • **: semua tingkat direktori (direktori tersembunyi dihilangkan secara default, gunakan Dkualifikasi glob untuk memasukkannya)
  • [1]: hanya yang pertama
  • :h: head modifier: ambil dirname.

Berlawanan dengan cd "$(find ...)"itu, ia juga berfungsi jika nama direktori berakhir dengan karakter baris baru. Keuntungan lain adalah bahwa Anda akan mendapatkan pesan kesalahan tidak cocok ketika tidak ada direktori yang cocok (sementara di sebagian besar shell cd ""tidak akan melakukan apa pun secara diam-diam).

Kekurangannya adalah ia akan merayapi keseluruhan /media/storagesebelum kembali.

Stéphane Chazelas
sumber
Dalam bash, cddengan beberapa args hanya melihat argumen pertama, jadi cd $(dirname /media/storage/**/Fedora)akan bekerja (dengan shopt -s globstar) jika tidak ada spasi di path. Untuk mendapatkannya dikutip benar, saya pikir array bash adalah yang paling mudah: target=(/media/storage/**/Fedora); cd "${target%/*}". Tetapi pada saat itu akan lebih cepat untuk menggunakan mouse untuk menyalin / menempelkan output yang ditemukan alih-alih menghasilkan secara interaktif.
Peter Cordes
2
@PeterCordes, banyak dirnameimplementasi tidak akan menerima lebih dari satu argumen. Perhatikan bahwa ini bukan spasi , karakter apa pun yang ada saat ini $IFS(spasi, tab, dan baris baru secara default) dan karakter wildcard. Perhatikan bahwa apakah bash's cdakan menerima lebih dari satu argumen tergantung pada bagaimana itu dikompilasi ( CD_COMPLAINSdi config-top.h). Orang dapat membayangkan bahwa versi masa depan bashjuga pada akhirnya akan mengimplementasikan fitur dua arg seperti di zsh.
Stéphane Chazelas
Terima kasih. Saya hanya melihat halaman manual GNU coreutils. Versi dirname sangat buruk; Saya hanya menyebutkannya sebagai sesuatu yang dapat Anda coba secara interaktif jika itu berhasil. Versi berbasis array saya tidak mengalami salah satu dari masalah itu, karena "${target%*/}"memperluas hanya elemen array pertama (dengan /Fedorastrip). Saya pikir versi itu sepenuhnya kuat terhadap karakter yang mungkin ada di pathname.
Peter Cordes