Use o nome da base para analisar uma lista de caminhos mantidos em um arquivo

9

Estou executando o Mac OSX e tentando usar a linha de comando para encontrar o número de arquivos que tenho com o mesmo nome.

Eu tentei usar o seguinte comando:

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

Isso não funciona! Quando eu faço o seguinte:

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

Então duplicate_files contém os caminhos de todos os meus arquivos. Então eu acho que o problema é com basename- ele não aceita entrada padrão. Eu tentei o seguinte:

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

mas, novamente, isso não parece funcionar. A pesquisa na internet parece não render muita alegria. Quaisquer pensamentos muito bem-vindos.

JohnB
fonte

Respostas:

16

basename opera em seu argumento de linha de comando, não lê da entrada padrão.

Você não precisa chamar o basenameutilitário, e é melhor não: tudo o que faria seria retirar a peça antes da última /e seria lento chamar um comando externo para cada entrada, você pode usar um processamento de texto utilitário em seu lugar.

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

Pode ser mais útil acompanhar o local dos arquivos. A classificação por nome facilita a localização de duplicatas, mas sortnão tem uma opção para usar o último campo. O que você pode fazer é copiar o último /campo separado para o início, classificar e usar um pouco de processamento ad hoc do awk para extrair e apresentar as duplicatas.

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

(Observe que presumo que nenhum dos seus nomes de arquivo contenha caracteres de nova linha.)

Gilles 'SO- parar de ser mau'
fonte
Super obrigado. Este é exatamente o que eu estava tentando fazer ... muito útil
JohnB
7

Por que não usar findrecursos internos para gerar apenas o nome do arquivo:

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

(assume o GNU find) ou pelo menos algo parecido com isto:

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

basename não pode ler via canal ou processar vários arquivos de uma só vez.

ps. Não há necessidade de especificar -name '*'se você deseja listar todos os arquivos. Esta é uma opção padrão.

pressa
fonte
Graças - se '-printf' não funcionar para OS X UNIX
JohnB
E quando tento a segunda versão que recebo basename: unknown primary or operator. Obrigado pela dica sobre-name "*"
JohnB
Isso é estranho. Eu posso ver -printfaté na página do manual do posix. Sobre o erro da segunda maneira, é causa de erro de digitação na minha resposta. Fixo. Você poderia tentar mais uma vez?
apressar
Também com -printfeu recebo o -printf: unknown primary or operator. Além disso, quando eu verifiquei o Unix em um livro de referência Nutshell ele lista como uma opção de GNU / Linux - não diz nada sobre OSX
JohnB
1
Na verdade, a melhor fonte estaria man findno seu console :) #
pressa
4

Isso parece funcionar para mim no OSX:

find ~ -type f -exec basename -a {} + | sort | uniq -d
rahmu
fonte
Sim - isso é muito obrigado - sem interesse, o que +significa o comando?
JohnB
2
Se isso for útil, considere fazer uma votação antecipada.
precisa
É - eu não posso votar até beacuase eu preciso de 15 reputação :-(
JohnB
@StephaneChazelas: De acordo com a página de manual do BSD basename , o executável pode usar várias strings como argumentos. Eu verifiquei novamente no OSX, ele funciona.
rahmu
1
Tudo bem, desculpe, eu estou corrigido. Eu não estava ciente dessa extensão BSD. No entanto, isso ainda falhará se houver exatamente dois arquivos. Você precisaria adicionar a -aopção de cobertura para esse caso também.
Stéphane Chazelas
2

Alternativas (não assume nova linha nos nomes dos arquivos):

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

Você pode usar xargscom basenamepara obter a saída desejada, assim:

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

Com uma versão recente bashque lida com matrizes associativas, os itens a seguir também lidam com nomes de caminho com novas linhas incorporadas:

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

Isso não usa utilitário externo.

Kusalananda
fonte