2017-03-04 3 views
1

Wie können mit dem ls-Befehl und den Optionen die sich wiederholenden Dateinamen in verschiedenen Verzeichnissen aufgelistet werden?Wie klassifiziere ich Dateien auf Linux-Servern nach ihren Namen?

+0

Ihre Frage ist besser geeignet für [Super User] (http://superuser.com/tour). [Stack Overflow ist eine Frage-Antwort-Site für professionelle und enthusiastische Programmierer] (http://stackoverflow.com/tour). – Cyrus

+0

@Cyrus, es ist ein fairer Kommentar. Es stellt sich heraus, dass es komplizierter ist als eine simple Bash-Globbing-Frage und erfordert einen vertretbaren Grad an Programmierung (wie geringfügig), obwohl der Fragesteller es zu dem Zeitpunkt, als er es fragte, nicht wusste. Also ich habe es hier beantwortet. Aber du bist richtig, wie gesagt, es ist mehr Super User orientiert. – eewanco

+0

Vergessen Sie nicht, eine Antwort als "akzeptiert" zu markieren, wenn es für Sie funktioniert! – eewanco

Antwort

1

Sie können dazu keinen einfachen Basisbefehl ls verwenden. Sie müssten eine Kombination aus anderen POSIX/Unix/GNU-Dienstprogrammen verwenden. Um zum Beispiel der doppelten Dateinamen finden zuerst:

find . -type f -exec basename "\{}" \; | sort | uniq -d > dupes 

Das bedeutet find alle Dateien (-type f) durch die gesamte Verzeichnishierarchie im aktuellen Verzeichnis (.) und Ausführen (-exec) den Befehl basename (der Streifen der Verzeichnisteil) für die gefundene Datei (\{}), Ende des Befehls (\;). Diese Dateien sortieren und drucken dann doppelte Zeilen (uniq -d). Das Ergebnis geht in die Datei dupes. Jetzt haben Sie die Dateinamen, die dupliziert sind, aber Sie wissen nicht, in welchem ​​Verzeichnis sie sich befinden. Verwenden Sie erneut find, um sie zu finden. Verwendung bash als Shell:

while read filename; do find . -name "$filename" -print; done < dupes 

Dies bedeutet Schleife durch (while), um alle Inhalte der Datei dupesread und in die Variable filename jeder Zeile. Führen Sie für jede Zeile erneut find aus und suchen Sie nach dem spezifischen -name der $filename und drucken Sie es aus (-print, aber es ist implizit, so dass dies redundant ist).

Wahrheit zu sagen Sie diese ohne Verwendung einer Zwischendatei kombinieren:

find . -type f -exec basename "\{}" \; | sort | uniq -d | while read filename; do find . -name "$filename" -print; done 

Wenn Sie nicht mit ihm vertraut sind, die | Operator Mittel, führen Sie den folgenden Befehl, um die Ausgabe des vorherigen Befehls als die Eingabe für den folgenden Befehl. Beispiel:

[email protected]:~$ mkdir test 
[email protected]:~$ cd test 
[email protected]:~/test$ mkdir 1 2 3 4 5 
[email protected]:~/test$ mkdir 1/2 2/3 
[email protected]:~/test$ touch 1/0000 2/1111 3/2222 4/2222 5/0000 1/2/1111 2/3/4444 
[email protected]:~/test$ find . -type f -exec basename "\{}" \; | sort | uniq -d | while read filename; do find . -name "$filename" -print; done 
./1/0000 
./5/0000 
./1/2/1111 
./2/1111 
./3/2222 
./4/2222 

Haftungsausschluss: Die Anforderung besagt, dass die Dateinamen alle Zahlen sind. Während ich versucht habe, den Code so zu gestalten, dass er Dateinamen mit Leerzeichen behandelt (und in Tests auf meinem System funktioniert), kann der Code brechen, wenn er auf Sonderzeichen, Zeilenumbrüche, Nullen oder andere ungewöhnliche Situationen stößt. Bitte beachten Sie, dass der Parameter -exec besondere Sicherheitsaspekte berücksichtigt und nicht von Root über beliebige Benutzerdateien verwendet werden sollte. Das vereinfachte Beispiel dient nur illustrativen und didaktischen Zwecken. Bitte konsultieren Sie Ihre man Seiten und relevante CERT Advisories für volle Sicherheit.

+0

Berühren Sie eine Datei mit dem Namen "file01 01 17" an mehreren Stellen und versuchen Sie Ihren Code. –

+0

@GeorgeVasiliou, funktioniert für mich, ich habe es versucht. Außerdem zeigte das Plakat an, dass seine Dateinamen alle Nummern waren. Es gab keine Anforderung, Dateinamen mit Leerzeichen zu behandeln, also habe ich den Code für dieses Szenario nicht speziell getestet. Ich füge jedoch einen Haftungsausschluss hinzu. – eewanco

0

Ich habe eine Funktion in meinem Bash-Profil (bash 4.4) für doppelte Dateien. Es stimmt, dass das Finden das richtige Werkzeug ist.

Ich benutze find kombiniert mit -print0 Optionen, die die Suchergebnisse mit Null-Zeichen anstelle von neuen Zeilen trennt (Standard-Suche Aktion). Jetzt kann ich alle Dateien unter dem aktuellen Verzeichnis und den Unterverzeichnissen abfangen.

Dies stellt sicher, dass die Ergebnisse korrekt sind, egal ob Dateinamen spezielle Zeichen wie Leerzeichen oder neue Zeilen enthalten (in einigen sehr seltenen Fällen). Anstatt doppelt zu suchen, finde ich ein Array und finde nur die doppelten Dateien in diesem Array. Dann grep das ganze Array mit den "Duplikaten" als Muster.

So etwas wie dies für meine Funktion ok funktioniert:

$ IFS= readarray -t -d '' fn< <(find . -name 'file*' -print0) 
# find all files and load them in an array using null delimiter 
$ printf '%s\n' "${fn[@]}" #print the array 
./tmp/file7 
./tmp/file14 
./tmp/file11 
./tmp/file8 
./tmp/file9 
./tmp/tmp2/file09 99 
./tmp/tmp2/file14.txt 
./tmp/tmp2/file15.txt 
./tmp/tmp2/file$100 
./tmp/tmp2/file14.txt.bak 
./tmp/tmp2/file15.txt.bak 
./tmp/file1 
./tmp/file4 
./file09 99 
./file14 
./file$100 
./file1 

$ dupes=$(LC_ALL=C sort <(printf '\<%s\>$\n' "${fn[@]##*/}") |uniq -d) 
#Locate duplicate files 
$ echo "$dupes" 
\<file$100\>$ #Mind this one with special char $ in filename 
\<file09 99\>$ #Mind also this one with spaces 
\<file14\>$ 
\<file1\>$ 
#I have on purpose enclose the results between \<...\> to force grep later to capture full words and avoid file1 to match file1.txt or file11 

$ grep -e "$dupes" <(printf '%s\n' "${fn[@]}") |awk -F/ '{print $NF,"==>",$0}' |LC_ALL=C sort 
file$100 ==> ./file$100   #File with special char correctly captured 
file$100 ==> ./tmp/tmp2/file$100 
file09 99 ==> ./file09 99  #File with spaces in name also correctly captured 
file09 99 ==> ./tmp/tmp2/file09 99 
file1 ==> ./file1 
file1 ==> ./tmp/file1 
file14 ==> ./file14    #other files named file14 like file14.txt and file14.txt.bak not captured since they are not duplicates. 
file14 ==> ./tmp/file14 

Tipps: auf der

  • Dieser <(printf '\<%s\>$\n' "${fn[@]##*/}") verwendet Prozess Substitution

    $ IFS= readarray -t -d '' fn< <(find . -name 'file*' -print0) 
    $ dupes=$(LC_ALL=C sort <(printf '\<%s\>$\n' "${fn[@]##*/}") |uniq -d) 
    $ grep -e "$dupes" <(printf '%s\n' "${fn[@]}") |awk -F/ '{print $NF,"==>",$0}' |LC_ALL=C sort 
    

    Dies ist ein Test Der Basisname des Finds wird mit bash in Parametererweiterungstechniken erstellt.

  • LC_ALL = C wird beim Sortieren benötigt, damit die Dateinamen korrekt sortiert werden.

  • In den Bash-Versionen vor 4.4 akzeptiert das Readarray keine Option -d (Delimiter). In diesem Fall können Sie die Ergebnisse in ein Array mit

    transformieren, während IFS = read -r-d '' res; tun fn + = ("$ res"); done < < (finden Sie .... -print0)

Verwandte Themen