ricerca senza distinzione tra maiuscole e minuscole di nomi file duplicati


Risposte:


14

Se hai utility GNU (o almeno un set che può gestire linee a terminazione zero), un'altra risposta ha un ottimo metodo:

find . -maxdepth 1 -print0 | sort -z | uniq -diz

Nota: l'output avrà stringhe con terminazione zero; lo strumento che usi per elaborare ulteriormente dovrebbe essere in grado di gestirlo.

In assenza di strumenti che si occupano di linee a terminazione zero, o se si desidera assicurarsi che il codice funzioni in ambienti in cui tali strumenti non sono disponibili, è necessario un piccolo script:

#!/bin/sh
for f in *; do
  find . -maxdepth 1 -iname ./"$f" -exec echo \; | wc -l | while read count; do
    [ $count -gt 1 ] && echo $f
  done
done

Cos'è questa follia? Vedi questa risposta per una spiegazione delle tecniche che lo rendono sicuro per i nomi di file folli.


1
Stavo per pubblicare un messaggio simile ... Ma una risposta peggiore :)
rozcietrzewiacz,

2
Hai davvero bisogno di -mindepth?
rozcietrzewiacz,

Sto usando Solaris. / Usr / bin / trova quello di cui stai parlando? Ho provato a usarlo e mi ha dato molti errori.
lamcro,

@lamcro No, Solaris non usa GNU find; Ho modificato la risposta per includere una soluzione non GNU.
Shawn J. Goff,

Ok. Devo semplicemente incollarlo in un file di testo e dargli i diritti di esecuzione?
lamcro,

12

Ci sono molte risposte complicate sopra, questa sembra più semplice e veloce di tutte:

find . -maxdepth 1 | sort -f | uniq -di

Se vuoi trovare nomi di file duplicati nelle sottodirectory, devi confrontare solo il nome del file, non l'intero percorso:

find . -maxdepth 2 -printf "%f\n" | sort -f | uniq -di

Modifica: Shawn J. Goff ha sottolineato che ciò fallirà se si hanno nomi di file con caratteri di nuova riga. Se stai usando le utility GNU, puoi far funzionare anche queste:

find . -maxdepth 1 -print0 | sort -fz | uniq -diz

L' opzione -print0(per trovare) e l' -zopzione (per ordinare e uniq) fanno sì che funzionino su stringhe con terminazione NUL, anziché su stringhe con fine riga. Poiché i nomi dei file non possono contenere NUL, questo funziona per tutti i nomi di file.


1
Ma vedi il mio commento sulla risposta di Shawn J. Goff, puoi aggiungere l'opzione -print0 per trovare e l'opzione -z per uniq e ordinare. Inoltre, vuoi -f anche sull'ordinamento. Quindi funziona. (Ho intenzione di modificarlo nella tua risposta, sentiti libero di ripristinare se non approvi)
derobert,

L'ultimo comando mi sta dando output senza ritorni a capo (il risultato è tutto in una riga). Sto usando Red Hat Linux per eseguire il comando. La prima riga di comando funziona meglio per me.
Dom

2

Ordina l'elenco dei nomi dei file in modo insensibile alle maiuscole e stampa i duplicati. sortha un'opzione per l'ordinamento senza distinzione tra maiuscole e minuscole. Lo stesso vale per GNU uniq, ma non per altre implementazioni, e tutto ciò che puoi fare uniqè stampare ogni elemento in una serie di duplicati tranne il primo che si incontra. Con gli strumenti GNU, supponendo che nessun nome di file contenga una nuova riga, esiste un modo semplice per stampare tutti gli elementi tranne uno in ciascun set di duplicati:

for x in *; do printf "%s\n" "$x"; done |
sort -f |
uniq -id

Portabilmente, per stampare tutti gli elementi in ogni set di duplicati, supponendo che nessun nome di file contenga una nuova riga:

for x in *; do printf "%s\n" "$x"; done |
sort -f |
awk '
    tolower($0) == tolower(prev) {
        print prev;
        while (tolower($0) == tolower(prev)) {print; getline}
    }
    1 { prev = $0 }'

Se devi inserire nomi di file contenenti newline, scegli Perl o Python. Si noti che potrebbe essere necessario modificare l'output, o meglio eseguire l'ulteriore elaborazione nella stessa lingua, poiché il codice di esempio riportato di seguito utilizza le nuove righe per separare i nomi nel proprio output.

perl -e '
    foreach (glob("*")) {push @{$f{lc($_)}}, $_}
    foreach (keys %f) {@names = @{$f{$_}}; if (@names > 1) {print "$_\n" foreach @names}}
'

Ecco una soluzione zsh pura. È un po 'prolisso, in quanto non esiste un modo integrato per mantenere gli elementi duplicati in un risultato array o glob.

a=(*)(N); a=("${(@io)a}")
[[ $#a -le 1 ]] ||
for i in {2..$#a}; do
  if [[ ${(L)a[$i]} == ${(L)a[$((i-1))]} ]]; then
    [[ ${(L)a[$i-2]} == ${(L)a[$((i-1))]} ]] || print -r $a[$((i-1))]
    print -r $a[$i]
  fi
done

1

Senza GNU find:

LANG=en_US ls | tr '[A-Z]' '[a-z]' | uniq -c | awk '$1 >= 2 {print $2}'


2
trè molto probabile che causi il caos su qualsiasi set di caratteri che utilizza più di un singolo byte per carattere. Solo i primi 256 caratteri di UTF-8 sono sicuri durante l'utilizzo tr. Da Wikipedia tr (Unix) .. La maggior parte delle versioni di tr, incluso GNU tre Unix classico tr, funzionano su SINGOLI BYTES e non sono conformi a Unicode ..
Peter.O

1
Aggiornamento al mio commento precedente .. solo i primi 128 caratteri di UTF-8 sono al sicuro. Tutti i caratteri UTF-8 al di sopra dell'intervallo ordinale 0..127 sono tutti multi-byte e possono avere valori di byte individuali in altri caratteri. Solo i byte nell'intervallo 0..127 hanno un'associazione uno a uno a un carattere univoco.
Peter

Plus uniqha una bandierina senza distinzione tra maiuscole e minuscole i.
Jamie Kitson,

1

Finalmente sono riuscito in questo modo:

find . | tr '[:upper:]' '[:lower:]' | sort | uniq -d

Ho usato findinvece di lscausa avevo bisogno del percorso completo (molte sottodirectory) incluso. Non ho trovato il modo di farlo ls.


2
Entrambi sorte uniqhanno flag di maiuscole / minuscole, rispettivamente f e i.
Jamie Kitson,

-1

Per chiunque voglia quindi rinominare ecc. Uno dei file:

find . -maxdepth 1 | sort -f | uniq -di | while read f; do echo mv "$f" "${f/.txt/_.txt}"; done
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.