Verwenden Sie den Basisnamen, um eine Liste der in einer Datei enthaltenen Pfade zu analysieren


9

Ich verwende Mac OSX und versuche, über die Befehlszeile die Anzahl der Dateien mit demselben Namen zu ermitteln.

Ich habe versucht, den folgenden Befehl zu verwenden:

find ~ -type f -name "*" -print | basename | sort | uniq -d > duplicate_files

Es funktioniert nicht! Wenn ich folgendes mache:

find ~ -type f -name "*" -print > duplicate_files

Dann enthält duplicate_files die Pfade aller meiner Dateien. Ich denke, das Problem liegt bei basename- es akzeptiert keine Standardeingabe. Ich habe dann folgendes versucht:

basename $(find ~ -type f -name "*" -print) > duplicate_files

aber auch das scheint nicht zu funktionieren. Die Suche im Internet scheint nicht viel Freude zu bereiten. Irgendwelche Gedanken sind herzlich willkommen.

Antworten:


16

basename arbeitet mit seinem Befehlszeilenargument und liest nicht aus der Standardeingabe.

Sie müssen das basenameDienstprogramm nicht aufrufen , und Sie sollten es besser nicht tun: Alles, was es tun würde, wäre, das Teil vor dem letzten /zu entfernen, und es wäre langsam, einen externen Befehl für jeden Eintrag aufzurufen. Sie können eine Textverarbeitung verwenden Dienstprogramm stattdessen.

find ~ -type f | sed 's!.*/!!' | sort | uniq -d

Es kann nützlicher sein, den Speicherort der Dateien zu verfolgen. Das Sortieren nach Namen erleichtert das Auffinden von Duplikaten, sortbietet jedoch keine Möglichkeit, das letzte Feld zu verwenden. Was Sie tun können, ist, das zuletzt /getrennte Feld an den Anfang zu kopieren , dann zu sortieren und dann ein wenig Ad-hoc-Awk-Verarbeitung zu verwenden, um die Duplikate zu extrahieren und zu präsentieren.

find ~ -type f |
sed 's!.*/\(.*\)!\1/&!' |   # copy the last field to the beginning
sort -t/ -k1,1 |
cut -d/ -f2- |   # remove the extra first field (could be combined with awk below)
awk -F / '{
    if ($NF == name) {
        if (previous != "") {print previous; previous = ""}
        print
    } else {
        previous = $0
        name = $NF
    }
'

(Beachten Sie, dass ich davon ausgehe, dass keiner Ihrer Dateinamen Zeilenumbrüche enthält.)


Super danke. Dies ist genau das, was ich versucht habe ... sehr nützlich
JohnB

7

Warum nicht integrierte findFunktionen verwenden, um nur den Dateinamen auszugeben:

find ~ -type f -printf '%f\n' | sort | uniq -c

(nimmt GNU an find) oder zumindest so etwas:

find ~ -exec basename {} \; | sort | uniq -c

basename kann nicht über Pipe lesen oder mehrere Dateien gleichzeitig verarbeiten.

ps. Sie müssen nicht angeben, -name '*'ob Sie alle Dateien auflisten möchten. Dies ist eine Standardoption.


Danke - '-printf' funktioniert nicht für OS X UNIX
JohnB

Und wenn ich die zweite Version versuche, bekomme ich basename: unknown primary or operator. Vielen Dank für den Tipp auf-name "*"
JohnB

Das ist seltsam. Ich kann -printfsogar in der Posix-Manpage sehen. Über den Fehler mit dem zweiten Weg ist es Ursache für Tippfehler in meiner Antwort. Fest. Könnten Sie es bitte noch einmal versuchen?
Eile

Auch mit -printfbekomme ich die -printf: unknown primary or operator. Auch als ich das Unix in einem Nutshell-Nachschlagewerk überprüft habe, wird es als GNU / Linux-Option aufgeführt - sagt nichts über OSX aus
JohnB

1
Eigentlich wäre die beste Quelle man findin Ihrer Konsole :)
Eile

4

Dies scheint für mich unter OSX zu funktionieren:

find ~ -type f -exec basename -a {} + | sort | uniq -d

Ja - das ist ein großer Dank - aus Interesse, was bedeutet das +im Befehl?
JohnB

2
Ist dies nützlich, ziehen Sie bitte eine Abstimmung in Betracht.
Verdächtiger

Es ist - ich kann nicht abstimmen, weil ich 15 Ruf brauche :-(
JohnB

@StephaneChazelas: Laut der Manpage für den BSD-Basisnamen kann die ausführbare Datei mehrere Zeichenfolgen als Argumente verwenden. Ich habe OSX doppelt überprüft, es funktioniert.
Rahmu

1
Okay, sorry, ich stehe korrigiert da. Diese BSD-Erweiterung war mir nicht bekannt. Dies schlägt jedoch immer noch fehl, wenn genau zwei Dateien vorhanden sind. Sie müssten die -aOption hinzufügen , um auch diesen Fall abzudecken.
Stéphane Chazelas

2

Alternativen (setzt keine neue Zeile in den Dateinamen voraus):

find ~ -type f | awk -F/ '{print $NF}' | sort | uniq -d

2

Sie können xargsmit verwenden basename, um die gewünschte Ausgabe zu erhalten, wie folgt:

find ~ -type f -name "*" -print | xargs -l basename | sort | uniq -d > duplicate_files

0

Mit einer neueren Version bash, die assoziative Arrays verarbeitet, werden im Folgenden zusätzlich Pfadnamen mit eingebetteten Zeilenumbrüchen behandelt:

#!/bin/bash

topdir=$HOME

shopt -s globstar  # enable the ** glob

declare -A count

# count the number of times each filename (base name) occurs
for pathname in "$topdir"/**; do
    # skip names that are not regular files (or not symbolic links to such files)
    [ ! -f "$pathname" ] && continue

    # get the base name
    filename=${pathname##*/}

    # add one to this base name's count
    count[$filename]=$(( ${count[$filename]} + 1 ))
done

# go through the collected names and print any name that
# has a count greater than one
for filename in "${!count[@]}"; do
    if [ "${count[$filename]}" -gt 1 ]; then
        printf 'Duplicate filename: %s\n' "$filename"
    fi
done

Dies verwendet kein externes Dienstprogramm.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.