Suchen Sie rekursiv nach Dateien mit einer bestimmten Erweiterung

437

Ich versuche mit meiner Bash (neueste Ubuntu LTS-Version) alle Dateien mit einer bestimmten Erweiterung in einem Verzeichnis und seinen Unterverzeichnissen zu finden.

Folgendes steht in einer Skriptdatei:

#!/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"

Wenn ich dieses Skript im Terminal starte, heißt es leider:

[: 29: in: unexpected operator

(mit $extensionstatt 'in')

Was ist hier los, wo ist der Fehler? Aber diese geschweifte Klammer

Flip
quelle
2
Der Fehler stammt von einer fehlenden '{'
Spitzmaus

Antworten:

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

ist etwas kürzer als das Ganze (und sicherer - behandelt Leerzeichen in Dateinamen und Verzeichnisnamen).

Ihr Skript schlägt wahrscheinlich fehl, wenn Einträge, .deren Name kein enthält , $extensionleer sind.

Matte
quelle
16
Ja, findist standardmäßig rekursiv. Sie können die Tiefen begrenzen, wenn Sie möchten (siehe Manpage).
Mat
1
Ich möchte alle gefundenen Dateien als Argumente an eine JAR-Datei übergeben. Wie kann das durchgeführt werden?
Flip
8
@flip: das ist eine andere frage. Stellen Sie eine neue Frage, in der genau angegeben ist, was Sie tun möchten und was Sie bisher versucht haben.
Mat
Eine kleine Korrektur: Verwenden Sie '* .in' oder \ *. In anstelle von "* .in", da doppelte Anführungszeichen die Shell-Erweiterung nicht verhindern. Dh Ihr Skript funktioniert nicht richtig, wenn sich im aktuellen Verzeichnis eine Datei mit der Erweiterung .in befindet.
Shnatsel
4
@Shnatsel: doppelte Anführungszeichen verhindern die Shell-Erweiterung. Versuch es.
Mat
188
find {directory} -type f -name '*.extension'

Beispiel: Um alle csvDateien im aktuellen Verzeichnis und seinen Unterverzeichnissen zu finden, verwenden Sie:

find . -type f -name '*.csv'
Mohammad AlQanneh
quelle
60

Die Syntax, die ich verwende, unterscheidet sich ein wenig von der von @Matt vorgeschlagenen:

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

(Es ist ein Tastendruck weniger).

Scott C Wilson
quelle
1
Matts Skript funktioniert auch nicht, wenn sich im aktuellen Verzeichnis eine Datei mit der Erweiterung .in befindet, während Ihre noch funktioniert. Siehe stackoverflow.com/questions/5927369/…
Shnatsel
4
@Shnatsel dieser Kommentar (und damit dein Kommentar) ist einfach falsch.
gniourf_gniourf
1
@gniourf_gniourf Sie sollten eine Referenz für Ihre Aussage angeben, sonst könnte man einfach argumentieren: "Nein, Sie liegen falsch". Aber tatsächlich haben Sie Recht: gnu.org/software/bash/manual/html_node/Double-Quotes.html
Murmel
@ user1885518: Ich denke, dass es der Typ sein sollte, der behauptet, dass das Skript nicht funktioniert, der einige Beispiele liefern sollte, bei denen das Skript fehlschlägt. Das mache ich, wenn ich Kommentare hinterlasse, in denen fehlerhafte Skripte vorhanden sind: Es geht normalerweise um Anführungszeichen und Dateinamen, die Leerzeichen, Zeilenumbrüche, Globs usw. enthalten, und ich erkläre speziell, warum sie fehlerhaft sind.
gniourf_gniourf
2
Das Bereitstellen von Referenzen ist in einer Diskussion immer ein guter Weg, es hängt nicht davon ab, wer der Erste war. Er sollte, du solltest.
Murmel
14

Ohne zu verwenden find:

du -a $directory | awk '{print $2}' | grep '\.in$'
rtrn
quelle
3
Das grepist hier nicht wirklich notwendig. awkhat reguläre Ausdrücke und könnte seine Ausgabe auf Werte beschränken, die einem Muster entsprechen.
Kenster
Diese Methode ist äußerst nützlich, wenn Sie 100 Terabyte durchlaufen. Die Verarbeitung des Befehls "Suchen" dauert zu lange. Dies beginnt sofort.
Protonova
1
awk|grepist ein Anti-Muster. Lass awk das Greppen machen.
Jens
10
  1. Es {fehlt ein nachbrowsefolders ()
  2. Alles $insollte sein$suffix
  3. Die Linie mit cutbekommt nur den mittleren Teil vonfront.middle.extension . Sie sollten Ihr Shell-Handbuch auf ${varname%%pattern}und Freunde lesen .

Ich gehe davon aus, dass Sie dies als Übung in Shell-Skripten tun, ansonsten die find bereits vorgeschlagene Lösung der richtige Weg.

Verwenden Sie, um die korrekte Shell-Syntax zu überprüfen, ohne ein Skript auszuführen sh -n scriptname.

Jens
quelle
10
find "$PWD" -type f -name "*.in"
kip2
quelle
7

Obwohl die Verwendung von findBefehlen hier nützlich sein kann, bietet die Shell selbst Optionen, um diese Anforderung ohne Tools von Drittanbietern zu erfüllen. Dasbash Shell bietet eine erweiterte Glob-Unterstützungsoption, mit der Sie die Dateinamen unter rekursiven Pfaden abrufen können, die mit den gewünschten Erweiterungen übereinstimmen.

Die erweiterte Option extglobmuss mit der folgenden shoptOption festgelegt werden. Die Optionen werden mit der -sUnterstützung aktiviert und mit dem -uFlag deaktiviert . Darüber hinaus können Sie einige weitere Optionen verwenden, z. B. nullglobbei denen ein nicht übereinstimmender Glob vollständig weggefegt und durch einen Satz von Nullwörtern ersetzt wird. Und globstardas ermöglicht es, alle Verzeichnisse zu durchsuchen

shopt -s extglob nullglob globstar

Jetzt müssen Sie nur noch den Glob-Ausdruck bilden, um die Dateien einer bestimmten Erweiterung einzuschließen. Dies können Sie wie folgt tun. Wir verwenden ein Array, um die Glob-Ergebnisse zu füllen, da die Dateinamen mit Sonderzeichen intakt bleiben und nicht durch Wortaufteilung durch die Shell beschädigt werden, wenn sie richtig zitiert und erweitert werden.

Zum Beispiel, um alle *.csvDateien in den rekursiven Pfaden aufzulisten

fileList=(**/*.csv)

Die Option **besteht darin, die Unterordner zu durchsuchen, und *.csvist eine globale Erweiterung, um alle Dateien der genannten Erweiterungen einzuschließen . Jetzt zum Drucken der eigentlichen Dateien einfach tun

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

Die Verwendung eines Arrays und eine ordnungsgemäße Erweiterung in Anführungszeichen ist der richtige Weg, wenn Sie in Shell-Skripten verwendet werden. Für die interaktive Verwendung können Sie jedoch einfach lsden glob-Ausdruck als verwenden

ls -1 -- **/*.csv

Dies könnte sehr gut erweitert werden, um mehrere Dateien abzugleichen, dh Dateien, die mit mehreren Erweiterungen enden (dh ähnlich dem Hinzufügen mehrerer Flags im findBefehl). Stellen Sie sich zum Beispiel vor , Sie müssten alle rekursiven Bilddateien abrufen, dh Erweiterungen *.gif, *.pngund *.jpgalles, was Sie brauchen, ist

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

Dies könnte sehr gut erweitert werden, um auch negative Ergebnisse zu erzielen. Mit der gleichen Syntax könnte man die Ergebnisse des Glob verwenden, um Dateien eines bestimmten Typs auszuschließen. Angenommen, Sie möchten Dateinamen mit den oben genannten Erweiterungen ausschließen

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

Das Konstrukt !()ist eine Negationsoperation, bei der keine der darin aufgeführten Dateierweiterungen enthalten ist, und es |handelt sich um einen Wechseloperator, wie er in der Bibliothek für erweiterte reguläre Ausdrücke verwendet wird, um eine ODER-Übereinstimmung der Globs durchzuführen.

Beachten Sie, dass diese erweiterte Glob-Unterstützung in der POSIX-Bourne-Shell nicht verfügbar ist und nur für neuere Versionen von spezifisch ist bash. Wenn Sie also die Portabilität der Skripte in Betracht ziehen, die unter POSIX und bashShells ausgeführt werden, ist diese Option nicht richtig.

Inian
quelle
6

Um alle pom.xmlDateien in Ihrem aktuellen Verzeichnis zu finden und auszudrucken, können Sie Folgendes verwenden:

find . -name 'pom.xml' -print
Bharat Yadav
quelle
1
find $directory -type f -name "*.in"|grep $substring
Sergiu
quelle
0
for file in "${LOCATION_VAR}"/*.zip
do
  echo "$file"
done 
Avinash Kumar Mishra
quelle
1
Während dieser Code die Frage möglicherweise beantwortet, verbessert die Bereitstellung eines zusätzlichen Kontexts darüber, warum und / oder wie dieser Code die Frage beantwortet, ihren langfristigen Wert.
Rollstuhlfahrer