Cerca ricorsivamente file con un'estensione specifica


437

Sto cercando di trovare tutti i file con un'estensione specifica in una directory e nelle sue sottodirectory con la mia bash (ultima versione di Ubuntu LTS).

Questo è ciò che è scritto in un file di script:

#!/bin/bash

directory="/home/flip/Desktop"
suffix="in"

browsefolders ()
  for i in "$1"/*; 
  do
    echo "dir :$directory"
    echo "filename: $i"
    #   echo ${i#*.}
    extension=`echo "$i" | cut -d'.' -f2`
    echo "Erweiterung $extension"
    if     [ -f "$i" ]; then        

        if [ $extension == $suffix ]; then
            echo "$i ends with $in"

        else
            echo "$i does NOT end with $in"
        fi
    elif [ -d "$i" ]; then  
    browsefolders "$i"
    fi
  done
}
browsefolders  "$directory"

Sfortunatamente, quando avvio questo script nel terminale, si dice:

[: 29: in: unexpected operator

(con $extensioninvece di 'in')

Cosa sta succedendo qui, dov'è l'errore? Ma questa parentesi graffa


2
L'errore deriva da un '{' mancante
shrewmouse del

Risposte:


750
find $directory -type f -name "*.in"

è un po 'più breve di tutto (e più sicuro - si occupa di spazi bianchi nei nomi di file e directory).

Lo script probabilmente non riesce per le voci che non hanno un .nome nel loro nome, rendendo $extensionvuoto.


16
sì, findè ricorsivo per impostazione predefinita. puoi limitare le profondità se vuoi (vedi la pagina man).
Mat

1
Vorrei passare tutti i file trovati come argomenti a un file jar. Come può essere eseguito?
capovolgi

8
@flip: questa è una domanda diversa. Pubblica una nuova domanda, specificando esattamente cosa ti piacerebbe fare e cosa hai provato finora.
Mat

Una piccola correzione: usa '* .in' o \ *. In invece di "* .in" perché le doppie virgolette non impediscono l'espansione della shell. Cioè il tuo script non funzionerà correttamente se c'è un file con estensione .in nella directory corrente.
Shnatsel,

4
@Shnatsel: le virgolette doppie impediscono l'espansione della shell. Provalo.
Mat

188
find {directory} -type f -name '*.extension'

Esempio: per trovare tutti i csvfile nella directory corrente e nelle sue sottodirectory, utilizzare:

find . -type f -name '*.csv'

60

La sintassi che uso è un po 'diversa da quella suggerita da @Matt:

find $directory -type f -name \*.in

(è un tasto in meno).


1
Anche lo script di Matt non funzionerà se c'è un file con estensione .in nella directory corrente, mentre il tuo funzionerebbe comunque. Vedere stackoverflow.com/questions/5927369/...
Shnatsel

4
@Shnatsel questo commento (e quindi il tuo) è chiaramente sbagliato.
gniourf_gniourf,

1
@gniourf_gniourf Dovresti fornire qualche riferimento per la tua affermazione, altrimenti si potrebbe semplicemente sostenere: "No, ti sbagli". Ma in effetti hai ragione: gnu.org/software/bash/manual/html_node/Double-Quotes.html
Murmel

@ user1885518: Penso che dovrebbe essere il ragazzo che afferma che lo script non funziona che dovrebbe fornire alcuni esempi in cui lo script ha esito negativo. Questo è quello che faccio quando lascio commenti in cui sono presenti script non funzionanti: di solito si tratta di citazioni e nomi di file contenenti spazi, newline, globs, ecc. E spiego in modo specifico perché è rotto.
gniourf_gniourf,

2
Fornire riferimenti è sempre un buon modo in una discussione, non dipende da chi è stato il primo. Dovrebbe, dovresti.
Murmel,

14

Senza usare find:

du -a $directory | awk '{print $2}' | grep '\.in$'

3
Non grepè davvero necessario qui. awkha espressioni regolari e potrebbe limitare il suo output a valori corrispondenti a un modello.
Kenster,

Questo metodo è estremamente utile se stai attraversando centinaia di terabyte. Il comando Trova richiede troppo tempo per l'elaborazione. Questo inizia immediatamente.
Protonova,

1
awk|grepè un anti-pattern. Lascia che Awk faccia il grepping.
Jens,

10
  1. Manca un {dopobrowsefolders ()
  2. Tutto $indovrebbe essere$suffix
  3. La linea con cutti dà solo la parte centrale di front.middle.extension. Dovresti leggere il manuale della shell ${varname%%pattern}e gli amici.

Presumo che tu faccia questo come un esercizio di shell scripting, altrimenti il find soluzione già proposta è la strada da percorrere.

Per verificare la corretta sintassi della shell, senza eseguire uno script, utilizzare sh -n scriptname.



7

Sebbene l'uso del findcomando possa essere utile qui, la shell stessa offre opzioni per raggiungere questo requisito senza strumenti di terze parti. La bashshell fornisce un'opzione di supporto globale estesa mediante la quale è possibile ottenere i nomi dei file in percorsi ricorsivi che corrispondono alle estensioni desiderate.

L'opzione estesa è quella extglobche deve essere impostata usando l' shoptopzione come di seguito. Le opzioni sono abilitate con il -ssupporto e disabilitate con lui -uflag. Inoltre, è possibile utilizzare un paio di opzioni in più, ad esempio nullglobin cui un globo senza pari viene completamente spazzato via, sostituito da un insieme di zero parole. E globstarciò consente di ricorrere in tutte le directory

shopt -s extglob nullglob globstar

Ora tutto ciò che devi fare è formare l'espressione glob per includere i file di una determinata estensione che puoi fare come di seguito. Usiamo un array per popolare i risultati glob perché, se quotati correttamente ed espansi, i nomi dei file con caratteri speciali rimarrebbero intatti e non si spezzerebbero a causa della divisione delle parole da parte della shell.

Ad esempio, per elencare tutti i *.csvfile nei percorsi ricorsivi

fileList=(**/*.csv)

L'opzione **è ricorrere attraverso le sottocartelle ed *.csvè l'espansione glob per includere qualsiasi file delle estensioni menzionate. Ora per stampare i file effettivi, basta

printf '%s\n' "${fileList[@]}"

L'uso di un array e l'esecuzione di una corretta espansione quotata è il modo giusto se utilizzato negli script di shell, ma per un uso interattivo, si può semplicemente usare lscon l'espressione glob come

ls -1 -- **/*.csv

Questo potrebbe benissimo essere espanso per abbinare più file, ad esempio il file che termina con più estensioni (cioè simile all'aggiunta di più flag nel findcomando). Ad esempio, considera un caso di necessità di ottenere tutti i file di immagine ricorsivi, ad esempio delle estensioni *.gif, *.pnge *.jpgtutto ciò che devi fare è

ls -1 -- **/+(*.jpg|*.gif|*.png)

Questo potrebbe benissimo essere ampliato per avere anche risultati negativi. Con la stessa sintassi, si potrebbero usare i risultati del glob per escludere file di un certo tipo. Supponi di voler escludere i nomi dei file con le estensioni sopra, puoi farlo

excludeResults=()
excludeResults=(**/!(*.jpg|*.gif|*.png))
printf '%s\n' "${excludeResults[@]}"

Il costrutto !()è un'operazione negativa per non includere nessuna delle estensioni di file elencate all'interno ed |è un operatore di alternanza proprio come usato nella libreria Extended Regular Expressions per eseguire una corrispondenza OR dei globs.

Si noti che questo supporto globale esteso non è disponibile nella shell bourne POSIX ed è puramente specifico per le versioni recenti di bash. Quindi se stai considerando la portabilità degli script in esecuzione su POSIX e bashshell, questa opzione non sarebbe corretta.


6

Per trovare tutti i pom.xmlfile nella directory corrente e stamparli, è possibile utilizzare:

find . -name 'pom.xml' -print

1
find $directory -type f -name "*.in"|grep $substring

0
for file in "${LOCATION_VAR}"/*.zip
do
  echo "$file"
done 

1
Mentre questo codice può rispondere alla domanda, fornendo un contesto aggiuntivo riguardo al perché e / o al modo in cui questo codice risponde alla domanda migliora il suo valore a lungo termine.
rollstuhlfahrer,
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.