Use basename para analizar una lista de rutas contenidas en un archivo

9

Estoy ejecutando Mac OSX e intento usar la línea de comando para encontrar la cantidad de archivos que tengo con el mismo nombre.

Traté de usar el siguiente comando:

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

No funciona! Cuando hago lo siguiente:

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

Entonces duplicate_files contiene las rutas de todos mis archivos. Así que creo que el problema es con basename: no acepta entradas estándar. Luego probé lo siguiente:

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

pero de nuevo eso no parece funcionar. La búsqueda en Internet no parece producir mucha alegría. Cualquier pensamiento más bienvenido.

JohnB
fuente

Respuestas:

16

basename opera en su argumento de línea de comando, no lee de la entrada estándar.

No necesita llamar a la basenameutilidad, y es mejor que no: todo lo que haría sería quitar la parte antes de la última /, y sería lento llamar a un comando externo para cada entrada, puede usar un procesamiento de texto utilidad en su lugar.

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

Puede ser más útil realizar un seguimiento de la ubicación de los archivos. Ordenar por nombre hace que sea más fácil localizar duplicados, pero sortno tiene una opción para usar el último campo. Lo que puede hacer es copiar el último /campo separado al principio, luego ordenar y luego usar un poco de procesamiento ad hoc awk para extraer y presentar los duplicados.

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

(Tenga en cuenta que supongo que ninguno de sus nombres de archivo contiene caracteres de nueva línea).

Gilles 'SO- deja de ser malvado'
fuente
Super gracias. Esto es exactamente lo que estaba tratando de hacer ... muy útil
JohnB
7

¿Por qué no utilizar las findfunciones incorporadas para generar solo el nombre de archivo?

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

(asume GNU find) o al menos algo como esto:

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

basename no puede leer a través de una tubería o procesar varios archivos a la vez

PD. No es necesario especificar -name '*'si desea enumerar todos los archivos. Esta es una opción por defecto.

prisa
fuente
Gracias - '-printf' no funciona para OS X UNIX
JohnB
Y cuando pruebo la segunda versión que obtengo basename: unknown primary or operator. Gracias por el consejo-name "*"
JohnB
Eso es extraño. Puedo ver -printfincluso en la página de manual de posix. Sobre el error con la segunda forma, es causa de error tipográfico en mi respuesta. Fijo. ¿Podrías intentarlo una vez más?
prisa el
También con -printfme sale el -printf: unknown primary or operator. Además, cuando revisé el libro de referencia Unix in a Nutshell, aparece como una opción de GNU / Linux - no dice nada sobre OSX
JohnB
1
En realidad, la mejor fuente estaría man finden tu consola :)
rush
4

Esto parece funcionar para mí en OSX:

find ~ -type f -exec basename -a {} + | sort | uniq -d
rahmu
fuente
Sí, esto es genial, gracias por interés, ¿qué +significa el comando?
JohnB
2
Si esto es útil, considere votarlo.
sospechoso
Es - No puedo votar porque necesito 15 reputación :-(
JohnB
@StephaneChazelas: Según la página del manual para BSD basename , el ejecutable puede tomar múltiples cadenas como argumentos. Revisé dos veces OSX, funciona.
rahmu
1
Muy bien, lo siento, estoy corregido. No estaba al tanto de esa extensión BSD. Sin embargo, eso todavía falla si hay exactamente dos archivos. Debería agregar la -aopción de cubrir ese caso también.
Stéphane Chazelas
2

Alternativas (supone que no hay nueva línea en los nombres de archivo):

find ~ -type f | awk -F/ '{print $NF}' | sort | uniq -d
Stéphane Chazelas
fuente
2

Puede usar xargscon basenamepara obtener la salida deseada, como esta:

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

Con una versión reciente de bashque maneja matrices asociativas, lo siguiente también manejaría nombres de ruta con nuevas líneas incrustadas:

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

Esto no utiliza ninguna utilidad externa.

Kusalananda
fuente