Utilizzare basename per analizzare un elenco di percorsi contenuti in un file


9

Sto eseguendo Mac OSX e sto cercando di utilizzare la riga di comando per trovare il numero di file che ho con lo stesso nome.

Ho provato a usare il seguente comando:

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

Non funziona! Quando faccio quanto segue:

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

Quindi duplicate_files contiene i percorsi di tutti i miei file. Quindi penso che il problema sia basename: non accetta input standard. Ho quindi provato quanto segue:

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

ma di nuovo non sembra funzionare. La ricerca su Internet non sembra dare molta gioia. Ogni pensiero è il benvenuto.

Risposte:


16

basename opera sul suo argomento da riga di comando, non legge dallo standard input.

Non è necessario chiamare l' basenameutilità, e meglio non farlo: tutto ciò che farebbe sarebbe rimuovere la parte prima dell'ultima /, e sarebbe lento chiamare un comando esterno per ogni voce, è possibile utilizzare un'elaborazione del testo utilità invece.

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

Potrebbe essere più utile tenere traccia della posizione dei file. L'ordinamento per nome semplifica l'individuazione dei duplicati, ma sortnon ha un'opzione per utilizzare l'ultimo campo. Quello che puoi fare è copiare l'ultimo /campo separato all'inizio, quindi ordinare e quindi utilizzare un po 'di elaborazione awk ad hoc per estrarre e presentare i duplicati.

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
    }
'

(Si noti che suppongo che nessuno dei nomi dei tuoi file contenga caratteri di nuova riga.)


Grazie super. Questo è esattamente quello che stavo cercando di fare ... molto utile
JohnB

7

Perché non usare le findfunzionalità integrate per generare solo il nome file:

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

(assume GNU find) o almeno qualcosa del genere:

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

basename impossibile leggere tramite pipe o elaborare più file contemporaneamente.

ps. Non è necessario specificare -name '*'se si desidera elencare tutti i file. Questa è un'opzione predefinita.


Grazie - '-printf' non funziona per OS X UNIX
JohnB

E quando provo la seconda versione ottengo basename: unknown primary or operator. Grazie per la -name "*"
segnalazione

È strano. Riesco a vedere -printfanche nella pagina man di posix. Sull'errore con il secondo modo, è causa di errore di battitura nella mia risposta. Fisso. Potresti provare ancora una volta?
precipita il

Anche con -printfottengo il -printf: unknown primary or operator. Inoltre, quando ho controllato Unix in un libro di
consultazione

1
In realtà la migliore fonte sarebbe man findnella tua console :)
precipita il

4

Questo sembra funzionare per me su OSX:

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

Sì - questo è un grande grazie - per interesse cosa +significa il comando?
John B

2
È utile, per favore, considera di votarlo.
suspectus

È - non posso votare perché ho bisogno di 15 reputazione :-(
JohnB

@StephaneChazelas: Secondo la pagina man per il basename di BSD , l'eseguibile può prendere più stringhe come argomenti. Ho ricontrollato OSX, funziona.
Rahmu,

1
Va bene scusa, sono corretto. Non ero a conoscenza di tale estensione BSD. Tuttavia, ciò non riesce se ci sono esattamente due file. Dovresti aggiungere l' -aopzione per la copertura anche per quel caso.
Stéphane Chazelas,

2

Alternative (non presuppone una nuova riga nei nomi dei file):

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

2

È possibile utilizzare xargscon basenameper ottenere l'output desiderato, in questo modo:

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

0

Con una versione recente di bashquesto gestisce array associativi, i seguenti gestiranno inoltre i percorsi con le nuove linee incorporate:

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

Questo non utilizza alcuna utilità esterna.

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.