Come posso usare `find` per andare alla directory di quel file


11

Voglio trovare un file e quindi inserire la directory che lo contiene. Ho provato find /media/storage -name "Fedora" | xargs cdma, ovviamente, ho l' is not a directoryerrore.

Come posso inserire la sua directory principale con un comando a una riga?


1
E se ci fossero più file da più posizioni?
Sergiy Kolodyazhnyy,

@Serg Sto cercando il file Fedora * .iso e so che ce n'è solo uno. Se ce ne fossero più di uno entrerebbe nella prima regia, credo
Hrvoje T

In base shopt -s globstar, potresti cd /media/storage/**/Fedora, ma ciò non smette di valutare il glob alla prima corrispondenza (quindi è più lento della soluzione di Steeldriver. Per un uso interattivo, ciò che normalmente farei è raggiungere il mouse e copiare / incollare il nome della directory, (e alt + backspace se necessario per eliminare i componenti del percorso finale che non volevo), ma se lo fai molto immagino che valga la pena fare una funzione shell.
Peter Cordes,

1
A proposito, xargs cdnon è possibile che funzioni. cdpuò funzionare solo come shell incorporata, perché deve modificare il contesto della shell stessa. Non è possibile che un xargsprocesso figlio possa farlo. IDK se questo è ciò che intendevi per "ovviamente", o se il percorso che findstampa contiene spazi, che sono divisi da xargs poiché non hai usato -d \no altro. Or find -exec {} \;.
Peter Cordes,

nota: non puoi correre cdcosì. cdè un bash incorporato, se cdfosse un comando separato, allora cambierebbe (il suo) dir, e poi uscirà (ritornando alla shell, che è nello stesso stato di prima, nessun cambio di dir).
ctrl-alt-delor,

Risposte:


14

Almeno se hai GNU find, puoi usare -printf '%h'per ottenere la directory

       %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
              ".".

Quindi probabilmente potresti farlo

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

L' -quitdovrebbe evitare che più argomenti cdnel caso più di un file corrisponde.


1
-quitinoltre non è necessariamente supportato. In NetBSD si chiama -exit, vedi unix.stackexchange.com/a/62883/117599
phk

2
Se non c'è printf, potresti invece fare -exec dirname?
Guy

@Guy buona idea sì, sembra che dovrebbe funzionare anche lui
steeldriver

6

Simile alla soluzione di Steeldriver ma usando -execdir(se il tuo lo findsupporta, come GNU o FreeBSD find) in combinazione con pwd:

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

-quitè facoltativo nel caso in cui vi sia un solo risultato e la scansione dell'intera directory non presenta alcun problema. Su NetBSD è -exite su OpenBSD non esiste.


E a cosa serve \;?
Hrvoje T,

1
@HrvojeT Proprio come -execracconta findla fine dei parametri per l'esecuzione del comando. Ma dal momento che vogliamo chiamare pwdsenza parametri qui, ci mettiamo \;subito dopo.
phk,

Ci sono findimplementazioni che supportano execdir ma non -printf %h? Mi sembra improbabile. Sfortunatamente nessuno dei due è richiesto da POSIX: /
Peter Cordes il

1
@PeterCordes di FreeBSD find: freebsd.org/cgi/man.cgi?find%281%29 (appena confermato su un'installazione di FreeBSD 11)
phk,

@PeterCordes Lo stesso per NetBSD ( netbsd.gw.com/cgi-bin/man-cgi?find++NetBSD-current ) e OpenBSD ( man.openbsd.org/OpenBSD-current/man1/find.1 ). L'ultimo non supporta -quit/ -exitaffatto però.
phk,

5

Puoi far trovare a run una nuova shell nella directory che trova.

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

, dopo di che la directory corrente sarà quella che contiene un file chiamato Fedora. ;)

Ovviamente questo fa qualcosa di simile a quello che vuoi se stai digitando i comandi in modo interattivo.


4

Con zsh:

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

a cdnella directory prima (in ordine alfabetico) che contiene un file chiamato Fedora.

  • **: qualsiasi livello di directory (le directory nascoste sono omesse per impostazione predefinita, utilizzare il Dqualificatore glob per includerle)
  • [1]: solo il primo
  • :h: modificatore di testa : prendi il nome dir.

Contrariamente a cd "$(find ...)", funziona anche se il nome della directory termina con un carattere di nuova riga. Un altro vantaggio è che riceverai un messaggio di errore senza corrispondenza quando non esiste una directory corrispondente (mentre nella maggior parte delle shell cd ""non farebbe nulla in silenzio).

Uno svantaggio è che dovrebbe strisciare per intero /media/storageprima di tornare.


In bash, cdcon più arg, guarda comunque solo il primo arg, quindi cd $(dirname /media/storage/**/Fedora)funzionerebbe (con shopt -s globstar) se non ci sono spazi nel percorso. Per farlo correttamente citato, credo che un array di bash è più facile: target=(/media/storage/**/Fedora); cd "${target%/*}". Ma a quel punto sarebbe stato più veloce utilizzare il mouse per copiare / incollare l'output di ricerca anziché elaborarlo interattivamente.
Peter Cordes,

2
@PeterCordes, molte dirnameimplementazioni non accettano più di un argomento. Nota che non sono spazi , sono tutti i caratteri attualmente presenti $IFS(spazio, tabulazione e nuova riga di default) e caratteri jolly. Si noti che se la bash's cdaccetteranno più di un argomento dipende da come è stato compilato ( CD_COMPLAINSin config-top.h). Si può immaginare che le versioni future di basheventualmente implementeranno anche la funzione arg due come in zsh.
Stéphane Chazelas,

Grazie. Ho appena guardato la manpage del nomname di coreutils GNU. La versione di dirname è comunque terribile; L'ho menzionato solo come qualcosa che potresti provare interattivamente nel caso funzionasse. La mia versione basata su array non presenta alcuno di questi problemi, poiché si "${target%*/}"espande solo al primo elemento dell'array (con lo /Fedorastripped). Penso che la versione sia completamente robusta rispetto a tutti i possibili caratteri nel percorso.
Peter Cordes,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.