Poiché tutti i file di input sono già ordinati, è possibile ignorare l'effettiva fase di ordinamento e utilizzarli solo sort -mper unire i file.
Su alcuni sistemi Unix (per quanto ne so solo Linux), potrebbe essere sufficiente farlo
sort -m *.words | uniq -d >dupes.txt
per ottenere le righe duplicate scritte nel file dupes.txt.
Per trovare i file da cui provengono queste linee, puoi farlo
grep -Fx -f dupes.txt *.words
Questo indicherà grepdi trattare le linee in dupes.txt( -f dupes.txt) come schemi di stringa fissi ( -F). greprichiederà inoltre che l'intera riga corrisponda perfettamente dall'inizio alla fine ( -x). Stampa il nome del file e la linea sul terminale.
Unices non Linux (o anche più file)
Su alcuni sistemi Unix, i nomi di file 30000 si espandono in una stringa che è troppo lunga per passare a una singola utility (il che significa sort -m *.wordsche fallirà Argument list too long, cosa che fa sul mio sistema OpenBSD). Anche Linux se ne lamenterà se il numero di file è molto più grande.
Trovare i duplicati
Ciò significa che nel caso generale (funzionerà anche con molti più di soli 30000 file), si dovrà "tagliare" l'ordinamento:
rm -f tmpfile
find . -type f -name '*.words' -print0 |
xargs -0 sh -c '
if [ -f tmpfile ]; then
sort -o tmpfile -m tmpfile "$@"
else
sort -o tmpfile -m "$@"
fi' sh
In alternativa, creare tmpfilesenza xargs:
rm -f tmpfile
find . -type f -name '*.words' -exec sh -c '
if [ -f tmpfile ]; then
sort -o tmpfile -m tmpfile "$@"
else
sort -o tmpfile -m "$@"
fi' sh {} +
Questo troverà tutti i file nella directory corrente (o sotto) i cui nomi corrispondono *.words. Per un pezzo di dimensioni appropriate di questi nomi alla volta, la cui dimensione è determinata da xargs/ find, li unisce nel tmpfilefile ordinato . Se tmpfileesiste già (per tutti tranne il primo blocco), questo file viene anche unito agli altri file nel blocco corrente. A seconda della lunghezza dei nomi dei file e della lunghezza massima consentita di una riga di comando, ciò potrebbe richiedere più o più di 10 singole esecuzioni dello script interno ( find/ xargslo farà automaticamente).
La shsceneggiatura "interna" ,
if [ -f tmpfile ]; then
sort -o tmpfile -m tmpfile "$@"
else
sort -o tmpfile -m "$@"
fi
usa sort -o tmpfileper l'output in tmpfile(questo non sovrascriverà tmpfileanche se anche questo è un input per sort) e -mper fare l'unione. In entrambi i rami, "$@"verrà espanso in un elenco di nomi di file citati singolarmente passati allo script da findo xargs.
Poi, basta eseguire uniq -dsu tmpfiledi ottenere tutte le linee che vengono duplicati:
uniq -d tmpfile >dupes.txt
Se ti piace il principio "DRY" ("Non ripetere te stesso"), puoi scrivere lo script interno come
if [ -f tmpfile ]; then
t=tmpfile
else
t=/dev/null
fi
sort -o tmpfile -m "$t" "$@"
o
t=tmpfile
[ ! -f "$t" ] && t=/dev/null
sort -o tmpfile -m "$t" "$@"
Da dove provengono?
Per gli stessi motivi di cui sopra, non possiamo usare grep -Fx -f dupes.txt *.wordsper trovare da dove provengono queste duplicazioni, quindi invece findriutilizziamo:
find . -type f -name '*.words' \
-exec grep -Fx -f dupes.txt {} +
Poiché non è necessario eseguire elaborazioni "complicate", è possibile invocare grepdirettamente -exec. L' -execopzione accetta un comando di utilità e inserirà i nomi trovati {}. Con +alla fine, findinserirà tanti argomenti al posto di {}quelli che la shell corrente supporta in ogni invocazione dell'utilità.
Per essere del tutto corretti, si potrebbe voler usare entrambi
find . -type f -name '*.words' \
-exec grep -H -Fx -f dupes.txt {} +
o
find . -type f -name '*.words' \
-exec grep -Fx -f dupes.txt /dev/null {} +
per essere sicuri che i nomi dei file siano sempre inclusi nell'output di grep.
La prima variante utilizza grep -Hper generare sempre nomi di file corrispondenti. L'ultima variante utilizza il fatto che grepincluderà il nome del file corrispondente se nella riga di comando viene fornito più di un file .
Ciò è importante dal momento che l'ultimo blocco di nomi di file inviato grepda findpuò effettivamente contenere solo un singolo nome file, nel qual caso grepnon lo menzionerebbe nei suoi risultati.
Materiale bonus:
Dissezione del comando find+ xargs+ sh:
find . -type f -name '*.words' -print0 |
xargs -0 sh -c '
if [ -f tmpfile ]; then
sort -o tmpfile -m tmpfile "$@"
else
sort -o tmpfile -m "$@"
fi' sh
find . -type f -name '*.words'genererà semplicemente un elenco di nomi di percorso dalla directory corrente (o inferiore) in cui ciascun percorso è quello di un normale file ( -type f) e che ha un componente nome file alla fine corrispondente *.words. Se si deve cercare solo la directory corrente , si può aggiungere -maxdepth 1dopo il ., prima -type f.
-print0farà in modo che tutti i percorsi trovati vengano emessi con un carattere \0( nul) come delimitatore. Questo è un personaggio che non è valido in un percorso Unix e ci consente di elaborare i nomi dei percorsi anche se contengono caratteri di nuova riga (o altre cose strane).
findconvoglia il suo output a xargs.
xargs -0leggerà l' \0elenco -delimitato di nomi di percorso ed eseguirà ripetutamente l'utilità data con blocchi di questi, assicurandosi che l'utilità sia eseguita con argomenti sufficienti per non far lamentare alla shell un elenco di argomenti troppo lungo, fino a quando non ci sono più input da find.
L'utilità invocata da xargsè shcon uno script fornito nella riga di comando come stringa usando il suo -cflag.
Quando si invocano gli sh -c '...some script...'argomenti seguenti, gli argomenti saranno disponibili per lo script $@, ad eccezione del primo argomento , che verrà inserito $0(questo è il "nome comando" che è possibile individuare, ad esempio topse si è abbastanza veloci). Questo è il motivo per cui inseriamo la stringa shcome primo argomento dopo la fine dello script reale. La stringa shè un argomento fittizio e potrebbe essere qualsiasi parola singola (alcuni sembrano preferire _o sh-find).
fi' sh?