Perché a volte find trova la corrispondenza con l'argomento path della riga di comando?


9

Su Linux,

cd /tmp
mkdir foo; cd foo

Adesso, correndo

find . -name 'foo'

non dà output. Mentre correre

find /tmp/foo -name 'foo'

Fornisce l'output /tmp/fooche non ha senso per me. Qualcuno può spiegare perché?


2
trova le seach all'interno del percorso indicato incluso come indicato. Quindi, se lo inserisci ./non corrispondefoo
Costas

@Costas: lo capisco. Quello che non capisco è perché ha fatto la differenza se do il percorso assoluto a find.
York,

@York In determinate situazioni non esiste un comportamento sempre corretto. Immagina un collegamento simbolico con il nome barche punta a un file fooesterno al percorso di ricerca. Abbinerà o no?
Hauke ​​Laging,

1
I .e /tmp/foonon sono gli stessi: sono due hard link diversi nella stessa directory; find /tmp/foo/. -name 'foo'non trova nulla neanche.
Jimmij,

@jimmij: quando corro find /tmp/foo -name 'foo', chiedevo a bash di trovare nella directory /tmp/foo, un file il cui nome è "pippo". Poiché la directory /tmp/fooè vuota, non avrebbe dovuto restituire nulla. Non capisco perché ritorni /tmp/foo. D'altra parte, quando corro find . -name 'foo', chiedevo a bash la stessa cosa, cioè trovare un file nella directory corrente (che è successo /tmp/foo), il cui nome è "pippo", e non restituisce nulla che abbia senso.
York,

Risposte:


15

findattraversa l'albero o gli alberi di directory specificati e valuta l'espressione data per ciascun file trovato. La traversata inizia nel percorso indicato. Ecco un riassunto di come find . -name foofunziona:

  • Primo percorso sulla riga di comando: .
    • Il nome di base ( .) corrisponde al modello foo? No, quindi non fare nulla.
      Accade così che /tmp/foosia un altro nome per la stessa directory. Ma findnon lo sa (e non dovrebbe cercare di scoprirlo).
    • Il percorso è una directory? Sì, quindi attraversalo. Enumerare le voci in .e, per ciascuna voce, eseguire il processo di attraversamento.
      • La directory è vuota: non contiene voci diverse da .e .., che findnon attraversa ricorsivamente. Quindi il lavoro è finito.

E find /tmp/foo:

  • Primo percorso sulla riga di comando: /tmp/foo
    • Il nome di base ( foo) corrisponde al modello foo? Sì, quindi la condizione corrisponde.
      • Non esiste alcuna azione associata a questa condizione, quindi esegui l'azione predefinita, che è quella di stampare il percorso.
    • Il percorso è una directory? Sì, quindi attraversalo. Enumerare le voci in /tmp/fooe, per ciascuna voce, eseguire il processo di attraversamento.
      • La directory è vuota: non contiene voci diverse da .e .., che findnon attraversa ricorsivamente. Quindi il lavoro è finito.

Succede così .e /tmp/foosono la stessa directory, ma ciò non è sufficiente per garantire che findabbia lo stesso comportamento su entrambi. Il findcomando ha modi per distinguere i percorsi nello stesso file; il -namepredicato è uno di questi. find /tmp/foo -name foocorrisponde alla directory iniziale e ai file sottostanti che viene chiamato foo. find . -name .corrisponde solo alla directory iniziale ( .non può mai essere trovato durante un attraversamento ricorsivo).


"non contiene voci diverse da. e ..., che trova non attraversa ricorsivamente." Presumo che "non attraversi ricorsivamente" si riferisca a "..". Se questo è corretto, cosa significherebbe "trasversare ricorsivamente" nel contesto di ".."?
Faheem Mitha,

@FaheemMitha "non attraversa ricorsivamente" si applica a entrambi .e ... (Se findattraversato in .modo ricorsivo, finirebbe per passare attraverso la directory ancora e ancora e ancora e ...) .e ..non sono solo notazioni speciali, appaiono come voci di directory.
Gilles 'SO- smetti di essere malvagio' l'

5

Non c'è normalizzazione degli argomenti della riga di comando prima dell'applicazione dei test. Pertanto i risultati differiscono a seconda del percorso utilizzato (se sono coinvolti collegamenti simbolici):

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

Nel "tuo caso" entrambe le chiamate darebbero lo stesso risultato che potrebbe essere (più) confuso. È possibile utilizzare -mindepth 1se si desidera ignorare i punti di partenza (potrebbe essere non POSIX).


-mindepth 1lavori. Tuttavia, ancora non capisco perché find . -name 'foo'e mi find /tmp/foo -name 'foo'comporto in modo diverso.
York,

2

(gnu) find mostra tutte le corrispondenze trovate nel percorso fornito al comando perché inizia il suo confronto con gli argomenti della riga di comando, scendendo più in profondità nella struttura della directory da lì (quindi, -maxdepth 0limita i test solo al livello di base o agli argomenti della riga di comando, mentre -mindepth 1salta gli argomenti della riga di comando come man findspiegato). Questo è il motivo per cui find /tmp/foo -name 'foo'produrrà una corrispondenza anche se la directory stessa è vuota.

find . -name 'foo'd'altra parte non produrrà alcun risultato perché .(punto) è un file speciale che agisce come un hardlink allo stesso inode di /tmp/foo- è come un nome file separato (sebbene speciale) e non un collegamento simbolico o un'espressione a cui è soggetta espansione del nome percorso da parte della shell. Pertanto, il primo test applicato da find agli argomenti della riga di comando nell'esempio dato non mostrerà alcuna corrispondenza, poiché in .effetti non corrisponde al modello di nome definito in -name 'foo'. Nemmeno /tmp/foo/.perché un test per un -namemodello viene eseguito solo sul basename del percorso (vedi man find), che è di nuovo qui ..

Anche se questo comportamento potrebbe non essere previsto o apparire intuitivo dal punto di vista dell'utente (e sì, all'inizio mi ha confuso), non costituisce un bug ma corrisponde alla logica e alla funzionalità descritte nelle pagine man e info per ( gnu) trova.


0

Ho provato a commentare la risposta di Gilles, ma era troppo lungo per entrare in un commento. Per questo motivo, lo sto inserendo come risposta alla mia domanda.

La spiegazione di Gilles (e anche quella di Shevek) è chiara e ha un senso. Il punto chiave qui è che non solo findcerca di far corrispondere i nomi dei file per i file all'interno dei percorsi indicati (ricorsivamente), ma cerca anche di abbinare il nome base dei percorsi dati stessi.

D'altra parte, c'è qualche prova che questo è il modo in cui findsi suppone funzioni, piuttosto che essere un bug? A mio avviso, sarebbe meglio se non rendesse i risultati incoerenti find .e find ABSOLUTE-PATHperché l'incoerenza è sempre fonte di confusione e potrebbe perdere molto tempo allo sviluppatore cercando di capire "cosa non andava". Nel mio caso, stavo scrivendo una sceneggiatura e il percorso è stato preso da una variabile. Quindi, affinché la mia sceneggiatura funzioni correttamente, quello che posso pensare di fare è scrivere find $path/. -name 'pattern'.

Infine, penso che sia possibile ottenere risultati coerenti findsostituendo sempre il .con la directory corrente prima di procedere.


-1

Non esiste alcun oggetto denominato foonella directory in cui è stata avviata la ricerca relativa.

È corretto supporre che gfindsia errato quando segnala /tmp/fooquando si utilizza il nome assoluto come directory iniziale.

Gfindha varie deviazioni dallo standard, sembra che tu ne abbia trovato un altro. Quando ti piace una soluzione conforme più standard, ti consiglio di sfindfar parte del schilytools.

-namesi applica ai risultati della ricerca nella directory. Nessuno dei due casi citati restituirà una voce di directory fooda readdir()un'operazione.


Sospetto che anche questo sia un bug. Ecco l'output di find --version: find (GNU findutils) 4.4.2 Copyright (C) 2007 Free Software Foundation, Inc. Licenza GPLv3 +: GNU GPL versione 3 o successiva < gnu.org/licenses/gpl.html >
York

Bene, ho controllato i tuoi comandi di esempio con find(certificato POSIX), gfinde sfind; il problema esiste solo quando si utilizza gfind. Su Linux, gfindnon è installato con il suo nome nativo ma con il nome find.
schily

3
È corretto per find /tmp/foo -name fool'output /tmp/foo. (Cc @York) Questo è il comportamento di OpenBSD find, GNU find, BusyBox find, Solaris finde qualsiasi altro conforme a POSIX find("Il primario deve valutare come vero se il nome di base del nome del file in esame corrisponde al modello (...)" - quando il nome del file è uno che viene passato come operando di percorso, nessuna readdirchiamata è coinvolta).
Gilles 'SO- smetti di essere malvagio' il
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.