Come utilizzare il comando Trova per elencare i nomi dei file ma non il percorso?

Jan 06 2021

Ho controllato alcune risposte online, ma non funzionano quando c'è un -nameparametro nel comando (forse sto facendo qualcosa di sbagliato, non sono sicuro). Quello che mi piacerebbe fare è elencare solo i nomi di file (non la directory / percorso) e, per complicare di più, ho lo stesso nome di file con un'estensione diversa, quindi devo filtrare il risultato usando -namee -o, il problema sono i suggerimenti che ho visto usare -printfo basename, e quelli non funzionano bene quando c'è -name *.txt -o name *.pdf. C'è un altro modo per risolvere questo problema?

Esempio dei miei file:

/dir1/dir2/dir3/dir4/
                    /file1.txt
                    /file1.pdf
                    /file1.ods ...etc.

Sono interessato solo a elencare uno o più tipi di file per elenco (utilizzando -nameper filtrare i risultati.

È possibile findeseguirlo utilizzando solo 1 comando o devo farlo in 2 passaggi (salvando il risultato in un file temporaneo, quindi filtrando / rimuovendo la directory dal file temporaneo)? Il risultato che sto cercando di ottenere è un file di testo con il nome del file per riga:

file1.txt
file1.pdf
file1.ods
file2.txt
file2.pdf
etc.

Il comando che stavo usando è

find /dir1/dir2/dir3/dir4/ -type f -name '*.txt' -o -name '*.pdf' -o -name '*.ods' -printf "%f\n"

Sto usando GNU find.

Aggiornare

Si è scoperto che devo mettere \(...\), come ricordato da αғsнιη

Risposte

3 αғsнιη Jan 06 2021 at 21:02

Quando si utilizzano più espressioni in modalità OR logico con -oè necessario racchiuderle tutte all'interno \( ... \), altrimenti il ​​risultato dell'ultima espressione viene sempre rispettato; e -printf '%f\n'viene utilizzato per stampare il nome dei file solo con qualsiasi directory principale rimossa.

find . -type f \( -name '*.pdf' -o -name '*.txt' \) -printf '%f\n'

vedere man findnella sezione OPERATORI su ( expr ):

OPERATORI
...

(espr)
Forza la precedenza. Poiché le parentesi sono speciali per la shell, normalmente sarà necessario citarle.

quindi puoi usare \( ... \)o '(' ... ')'(gli spazi bianchi dopo e prima delle parentesi sono importanti) per sfuggirli.

1 Kusalananda Jan 06 2021 at 22:58

Come già accennato, devi raggruppare i tuoi -nametest tra parentesi. Queste parentesi devono essere evase \( ... \)o citate come ad esempio '(' ... ')'per proteggerle dalla shell (dove altrimenti designerebbero una subshell).

Usando lo standard finde rendendo leggermente più semplice la gestione dei suffissi del nome del file:

set -- txt pdf ods

for suffix do
    set -- "$@" -o -name "*.$suffix"
    shift
done
shift

find /dir1/dir2/dir3/dir4 \( "$@" \) -type f -exec basename {} \;

Questo prima imposta i parametri posizionali sull'elenco dei suffissi dei nomi dei file che vorremmo vedere (questo potrebbe anche essere preso dalla riga di comando se si tratta di uno script) e costruisce l'elenco OR dei -nametest. Questo viene quindi utilizzato nella findriga di comando.

Il findcomando stesso chiama quindi l' basenameutilità per ogni nome trovato.

In alternativa, potremmo invece utilizzare una sostituzione di parametro per rimuovere tutto tranne il componente del nome file di ciascun percorso, ma per farlo dobbiamo passare i nomi di percorso a uno sh -cscript inline :

# [...] as before [...]

find /dir1/dir2/dir3/dir4 \( "$@" \) -type f -exec sh -c '
    for pathname do
        printf "%s\n" "${pathname##*/}"
    done' sh {} +

Mi aspetto che questo venga eseguito più velocemente della variazione utilizzando basename, almeno se ci sono molti nomi di file da gestire, sebbene con GNU basenamedi coreutils, potresti usare -ao mi --multiplepiace così:

find /dir1/dir2/dir3/dir4 \( "$@" \) -type f -exec basename -a {} +

Questo chiamerebbe il basenameminor numero di volte possibile. Ma poi di nuovo, se hai installato coreutils, potresti avere anche findutils, con GNU find, e potresti usare -printf '%f\n'per fare la stessa cosa.