Come ottenere il nome del file solo con "find" di Linux?


266

Sto usando trova a tutti i file nella directory, quindi ottengo un elenco di percorsi. Tuttavia, ho bisogno solo dei nomi dei file. cioè ottengo ./dir1/dir2/file.txte voglio ottenerefile.txt

Risposte:


359

In GNU findè possibile utilizzare i -printfparametri per questo, ad esempio:

find /dir1 -type f -printf "%f\n"

9
Chiaramente la risposta , ma manca di dettagli.
Jason McCreary,

25
questo è solo per GNU
Osama F Elias

3
Questo non funziona per me quando utilizzo più tipi di file (-o switch)
Urchin,

9
trovare: -printf: primario o operatore sconosciuto
holms

@Urchin Non c'è motivo per cui non dovresti avere la logica corretta (cioè -oha una precedenza inferiore a quella implicita -a, quindi spesso vorrai raggruppare le tue -oargomentazioni)
Reinstalla Monica, per favore,

157

Se la tua ricerca non ha un'opzione -printf puoi anche usare basename:

find ./dir1 -type f -exec basename {} \;

17
Citare il punto e virgola è un altro modo di ... {} ';'
chiarire le

28

Utilizzare ciò -execdirche contiene automaticamente il file corrente {}, ad esempio:

find . -type f -execdir echo '{}' ';'

Puoi anche usare $PWDinvece di .(su alcuni sistemi non produrrà un punto in più nella parte anteriore).

Se hai ancora un punto in più, in alternativa puoi eseguire:

find . -type f -execdir basename '{}' ';'

-execdir utility [argument ...] ;

Il -execdirprimario è identico al -execprimario con l'eccezione che l'utilità verrà eseguita dalla directory che contiene il file corrente .

Se utilizzato +invece di ;, {}viene sostituito con il maggior numero di percorsi possibili per ogni chiamata di utilità. In altre parole, stamperà tutti i nomi di file in una riga.


Sto ottenendo ./filenameinvece di filename. A seconda delle tue esigenze, potrebbe andare o no.
user276648

@ user276648 Prova con $PWDinvece di ..
Kenorb,

24

Se stai usando GNU find

find . -type f -printf "%f\n"

Oppure puoi usare un linguaggio di programmazione come Ruby (1.9+)

$ ruby -e 'Dir["**/*"].each{|x| puts File.basename(x)}'

Se hai voglia di una soluzione bash (almeno 4)

shopt -s globstar
for file in **; do echo ${file##*/}; done

Sono stato ispirato dalla tua risposta per aiutare sleske: serverfault.com/a/745968/329412
Nolwennig,

11

Se vuoi eseguire qualche azione solo sul nome del file, usare basenamepuò essere difficile.

Ad esempio questo:

find ~/clang+llvm-3.3/bin/ -type f -exec echo basename {} \; 

farà l'eco di basename /my/found/path. Non quello che vogliamo se vogliamo eseguire sul nome del file.

Ma puoi quindi xargsl'output. ad esempio per uccidere i file in una directory in base ai nomi in un'altra directory:

cd dirIwantToRMin;
find ~/clang+llvm-3.3/bin/ -type f -exec basename {} \; | xargs rm

non echo -find ~/clang+llvm-3.3/bin/ -type f -exec basename {} \;
commonpike

7

Su mac (BSD find) usare:

find /dir1 -type f -exec basename {} \;

1

-exece -execdirsono lenti, xargsè re.

$ alias f='time find /Applications -name "*.app" -type d -maxdepth 5'; \
f -exec basename {} \; | wc -l; \
f -execdir echo {} \; | wc -l; \
f -print0 | xargs -0 -n1 basename | wc -l; \
f -print0 | xargs -0 -n1 -P 8 basename | wc -l; \
f -print0 | xargs -0 basename | wc -l

     139
    0m01.17s real     0m00.20s user     0m00.93s system
     139
    0m01.16s real     0m00.20s user     0m00.92s system
     139
    0m01.05s real     0m00.17s user     0m00.85s system
     139
    0m00.93s real     0m00.17s user     0m00.85s system
     139
    0m00.88s real     0m00.12s user     0m00.75s system

xargsIl parallelismo aiuta anche.

Stranamente non riesco a spiegare l'ultimo caso di xargssenza -n1. Dà il risultato corretto ed è il più veloce¯\_(ツ)_/¯

( basenameaccetta solo 1 argomento path ma xargsli invierà tutti (in realtà 5000) senza -n1. non funziona su linux e openbsd, solo macOS ...)

Alcuni numeri più grandi da un sistema Linux per vedere come -execdiraiuta, ma ancora molto più lentamente di un parallelo xargs:

$ alias f='time find /usr/ -maxdepth 5 -type d'
$ f -exec basename {} \; | wc -l; \
f -execdir echo {} \; | wc -l; \
f -print0 | xargs -0 -n1 basename | wc -l; \
f -print0 | xargs -0 -n1 -P 8 basename | wc -l

2358
    3.63s real     0.10s user     0.41s system
2358
    1.53s real     0.05s user     0.31s system
2358
    1.30s real     0.03s user     0.21s system
2358
    0.41s real     0.03s user     0.25s system

1
solo un altro punto dati: a lungo andare su openbsd findè -execdirche diventa il più veloce in quanto la creazione di nuovi processi è un'operazione relativamente costosa.
meno

1

Onestamente basenamee le dirnamesoluzioni sono più facili, ma puoi anche dare un'occhiata:

find . -type f | grep -oP "[^/]*$"

o

find . -type f | rev | cut -d '/' -f1 | rev

o

find . -type f | sed "s/.*\///"

0

Come altri hanno sottolineato, è possibile combinare finde basename, ma per impostazione predefinita il basenameprogramma funzionerà solo su un percorso alla volta, quindi l'eseguibile dovrà essere lanciato una volta per ogni percorso (usando uno find ... -execo find ... | xargs -n 1), che potrebbe essere potenzialmente lento.

Se usi l' -aopzione on basename, allora può accettare più nomi di file in una singola chiamata, il che significa che puoi quindi utilizzare xargssenza il -n 1, per raggruppare i percorsi in un numero molto più piccolo di chiamate basename, che dovrebbero essere più efficienti.

Esempio:

find /dir1 -type f -print0 | xargs -0 basename -a

Qui ho incluso -print0e -0(che dovrebbe essere usato insieme), al fine di far fronte a qualsiasi spazio bianco all'interno dei nomi di file e directory.

Ecco un confronto temporale tra le versioni xargs basename -ae xargs -n1 basename. (Per un confronto simile a simile, i tempi riportati qui sono dopo una corsa fittizia iniziale, quindi entrambi vengono eseguiti dopo che i metadati del file sono già stati copiati nella cache I / O.) Ho reindirizzato l'output a cksumin entrambi i casi, solo per dimostrare che l'output è indipendente dal metodo utilizzato.

$ time sh -c 'find /usr/lib -type f -print0 | xargs -0 basename -a | cksum'
2532163462 546663

real    0m0.063s
user    0m0.058s
sys 0m0.040s

$ time sh -c 'find /usr/lib -type f -print0 | xargs -0 -n 1 basename | cksum' 
2532163462 546663

real    0m14.504s
user    0m12.474s
sys 0m3.109s

Come puoi vedere, è davvero molto più veloce evitare di lanciare basenameogni volta.


Leggendo la risposta di @minusf in modo più approfondito, vedo che su un Mac basenameaccetterà più nomi di file senza bisogno di ulteriori argomenti da riga di comando. L'uso di -aqui è su Linux. ( basename --versionmi dice basename (GNU coreutils) 8.28.)
Alaniwi,

-3

Ho trovato una soluzione (sulla pagina di makandracards) che fornisce solo il nome del file più recente:

ls -1tr * | tail -1

(grazie ad Arne Hartherz)

L'ho usato per cp:

cp $(ls -1tr * | tail -1) /tmp/

2
Questo non risponde affatto alla domanda.
Kenorb,
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.