AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / unix / Perguntas / 437052
Accepted
Kiwy
Kiwy
Asked: 2018-04-12 07:28:57 +0800 CST2018-04-12 07:28:57 +0800 CST 2018-04-12 07:28:57 +0800 CST

Otimização de script para encontrar nomes de arquivo duplicados em CSV enorme

  • 772

Tenho vários arquivos CSV de 1MB a 6GB gerados por inotifyscript com uma lista de eventos formatados como:
timestamp;fullpath;event;size.

Esses arquivos são formatados assim:

timestamp;fullpath;event;size
1521540649.02;/home/workdir/ScienceXMLIn/config.cfg;IN_OPEN;2324
1521540649.02;/home/workdir/ScienceXMLIn/config.cfg;IN_ACCESS;2324
1521540649.02;/home/workdir/ScienceXMLIn/config.cfg;IN_CLOSE_NOWRITE;2324
1521540649.02;/home/workdir/quad_list_14.json;IN_OPEN;2160
1521540649.03;/home/workdir/quad_list_14.json;IN_ACCESS;2160
1521540649.03;/home/workdir/quad_list_14.json;IN_CLOSE_NOWRITE;2160
1521540649.03;/home/workdir/ScienceXMLIn/masterbias_list.asc;IN_OPEN;70
1521540649.03;/home/workdir/ScienceXMLIn/masterbias_list.asc.1;IN_OPEN;80
1521540649.03;/home/workdir/ScienceXMLIn/masterbias_list.asc.2;IN_OPEN;70
1521540649.03;/home/workdir/otherfolder/quad_list_14.json;IN_OPEN;2160
1521540649.03;/home/workdir/otherfolder/quad_list_14.json;IN_CLOSE_NOWRITE;2160

Meu objetivo é identificar o arquivo com o mesmo nome que aparece em pastas diferentes.
Neste exemplo, o arquivo quad_list_14.jsonaparece nos formatos /home/workdir/otherfoldere /home/workdir/.

Minha saída desejada é simples apenas a lista de arquivos que aparece em mais de uma pasta, neste caso ficaria assim:

quad_list_14.json

Para fazer isso, escrevi este pequeno trecho de código:

#this line cut the file to only get unique filepath
PATHLIST=$(cut -d';' -f 2 ${1} | sort -u)
FILENAMELIST=""

#this loop build a list of basename from the list of filepath
for path in ${PATHLIST}
do
    FILENAMELIST="$(basename "${path}")
${FILENAMELIST}"
done

#once the list is build, I simply find the duplicates with uniq -d as the list is already sorted
echo "${FILENAMELIST}" | sort | uniq -d

Não use este código em casa, é terrível, eu deveria substituir este script por um onliner como este:

#this get all file path, sort them and only keep unique entry then
#remove the path to get the basename of the file 
#and finally sort and output duplicates entry.
cut -d';' -f 2 ${1} | sort -u |  grep -o '[^/]*$' | sort | uniq -d

Meu problema, porém, permanece e muitos arquivos e o mais curto leva 0,5 segundo, mas o mais longo leva 45 segundos em um SSD (e meu disco de produção não será tão rápido) para encontrar o nome do arquivo duplicado em uma pasta diferente.

Preciso melhorar esse código para torná-lo mais eficiente. Minha única limitação é que não consigo carregar totalmente os arquivos na RAM.

bash awk
  • 3 3 respostas
  • 106 Views

3 respostas

  • Voted
  1. Best Answer
    Stephen Kitt
    2018-04-12T07:39:20+08:002018-04-12T07:39:20+08:00

    O seguinte script AWK deve fazer o truque, sem usar muita memória:

    #!/usr/bin/awk -f
    
    BEGIN {
        FS = ";"
    }
    
    {
        idx = match($2, "/[^/]+$")
        if (idx > 0) {
            path = substr($2, 1, idx)
            name = substr($2, idx + 1)
            if (paths[name] && paths[name] != path && !output[name]) {
                print name
                output[name] = 1
            }
            paths[name] = path
        }
    }
    

    Ele extrai o caminho e o nome de cada arquivo e armazena o último caminho visto para cada nome. Se já havia visto outro caminho, ele gera o nome, a menos que já o tenha exibido.

    • 3
  2. Kusalananda
    2018-04-12T07:51:44+08:002018-04-12T07:51:44+08:00

    O principal problema com seu código é que você está coletando todos os nomes de caminho em uma variável e, em seguida, fazendo um loop para chamar basename. Isso torna lento.

    O loop também é executado sobre a variável expansion sem aspas ${PATHLIST}, o que seria imprudente se os nomes de caminho contivessem espaços ou caracteres de globbing de shell. Em bash(ou em outros shells que o suportam), seria usado um array.

    Sugestão:

    $ sed -e '1d' -e 's/^[^;]*;//' -e 's/;.*//' file.csv | sort -u | sed 's#.*/##' | sort | uniq -d
    quad_list_14.json
    

    O primeiro sedescolhe os nomes dos caminhos (e descarta a linha do cabeçalho). Isso também pode ser escrito como awk -F';' 'NR > 1 { print $2 }' file.csv, ou como tail -n +2 file.csv | cut -d ';' -f 2.

    O sort -unos fornece nomes de caminho exclusivos e o seguinte sednos fornece os nomes de base. O final sortcom uniq -dno final nos diz quais nomes de base são duplicados.

    O último sed 's#.*/##'que fornece os nomes de base é uma reminiscência da expansão do parâmetro ${pathname##*/}que é equivalente a $( basename "$pathname" ). Ele apenas exclui tudo até e incluindo o último /na string.

    A principal diferença do seu código é que, em vez do loop que chama basenamevárias vezes, um único sedé usado para produzir os nomes de base de uma lista de nomes de caminho.


    Alternativa para olhar apenas as IN_OPENentradas:

    sed -e '/;IN_OPEN;/!d' -e 's/^[^;]*;//' -e 's/;.*//' file.csv | sort -u | sed 's#.*/##' | sort | uniq -d
    
    • 3
  3. Kiwy
    2018-04-13T00:42:22+08:002018-04-13T00:42:22+08:00

    Obrigado a vocês dois por suas respostas e obrigado Isaac pelos comentários.
    Peguei todo o seu código e coloquei em um script stephen.awk kusa.she isaac.shdepois executei um pequeno benchmark como este:

    for i in $(ls *.csv)
    do
        script.sh $1
    done
    

    Com o comando timeeu os comparo e aqui estão os resultados:

    stephen.awk

    real    2m35,049s
    user    2m26,278s
    sys     0m8,495s
    

    stephen.awk : atualizado com /IN_OPEN/ antes do segundo bloco

    real    0m35,749s
    user    0m15,711s
    sys     0m4,915s
    

    kusa.sh

    real    8m55,754s
    user    8m48,924s
    sys     0m21,307s
    

    Atualizar com filtro em IN_OPEN:

    real    0m37,463s
    user    0m9,340s
    sys     0m4,778s
    

    Nota lateral:
    embora correto, eu tinha muitas linhas em branco geradas com sed, seu script era o único assim.

    isaac.sh

    grep -oP '^[^;]*;\K[^;]*' file.csv | sort -u | grep -oP '.*/\K.*' | sort | uniq -d
    real    7m2,715s
    user    6m56,009s
    sys     0m18,385s
    

    Com filtro ativado IN_OPEN:

    real    0m32,785s
    user    0m8,775s
    sys     0m4,202s
    

    meu roteiro

    real    6m27,645s
    user    6m13,742s
    sys     0m20,570s
    

    @Stephen você claramente venceu esta, com uma redução de tempo impressionante em um fator de 2,5.

    Embora depois de pensar um pouco mais, tive outra ideia, e se eu apenas olhar para o evento OPEN file, isso reduziria a complexidade e você não deveria acessar um arquivo ou escrevê-lo sem abri-lo primeiro, então fiz isso:

    #see I add grep "IN_OPEN" to reduce complexity
    PATHLIST=$(grep "IN_OPEN" "${1}" | cut -d';' -f 2 | sort -u)
    FILENAMELIST=""
    for path in ${PATHLIST}
    do
        FILENAMELIST="$(basename "${path}")
    ${FILENAMELIST}"
    done
    echo "${FILENAMELIST}" | sort | uniq -d
    

    Com esta única modificação que me deu o mesmo resultado, acabo com este timevalor:

    real    0m56,412s
    user    0m27,439s
    sys     0m9,928s
    

    E tenho certeza de que há muitas outras coisas que eu poderia fazer

    • 1

relate perguntas

  • Embaralhamento de arquivo de várias linhas

  • Problema estranho ao passar variáveis ​​do arquivo de texto

  • Enquanto a linha lê mantendo os espaços de escape?

  • ordem de substituição de processos `te` e `bash`

  • Execute um script muito lento até que seja bem-sucedido

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Como exportar uma chave privada GPG e uma chave pública para um arquivo

    • 4 respostas
  • Marko Smith

    ssh Não é possível negociar: "nenhuma cifra correspondente encontrada", está rejeitando o cbc

    • 4 respostas
  • Marko Smith

    Como podemos executar um comando armazenado em uma variável?

    • 5 respostas
  • Marko Smith

    Como configurar o systemd-resolved e o systemd-networkd para usar o servidor DNS local para resolver domínios locais e o servidor DNS remoto para domínios remotos?

    • 3 respostas
  • Marko Smith

    Como descarregar o módulo do kernel 'nvidia-drm'?

    • 13 respostas
  • Marko Smith

    apt-get update error no Kali Linux após a atualização do dist [duplicado]

    • 2 respostas
  • Marko Smith

    Como ver as últimas linhas x do log de serviço systemctl

    • 5 respostas
  • Marko Smith

    Nano - pule para o final do arquivo

    • 8 respostas
  • Marko Smith

    erro grub: você precisa carregar o kernel primeiro

    • 4 respostas
  • Marko Smith

    Como baixar o pacote não instalá-lo com o comando apt-get?

    • 7 respostas
  • Martin Hope
    rocky Como exportar uma chave privada GPG e uma chave pública para um arquivo 2018-11-16 05:36:15 +0800 CST
  • Martin Hope
    Wong Jia Hau ssh-add retorna com: "Erro ao conectar ao agente: nenhum arquivo ou diretório" 2018-08-24 23:28:13 +0800 CST
  • Martin Hope
    Evan Carroll status systemctl mostra: "Estado: degradado" 2018-06-03 18:48:17 +0800 CST
  • Martin Hope
    Tim Como podemos executar um comando armazenado em uma variável? 2018-05-21 04:46:29 +0800 CST
  • Martin Hope
    Ankur S Por que /dev/null é um arquivo? Por que sua função não é implementada como um programa simples? 2018-04-17 07:28:04 +0800 CST
  • Martin Hope
    user3191334 Como ver as últimas linhas x do log de serviço systemctl 2018-02-07 00:14:16 +0800 CST
  • Martin Hope
    Marko Pacak Nano - pule para o final do arquivo 2018-02-01 01:53:03 +0800 CST
  • Martin Hope
    Kidburla Por que verdadeiro e falso são tão grandes? 2018-01-26 12:14:47 +0800 CST
  • Martin Hope
    Christos Baziotis Substitua a string em um arquivo de texto enorme (70 GB), uma linha 2017-12-30 06:58:33 +0800 CST
  • Martin Hope
    Bagas Sanjaya Por que o Linux usa LF como caractere de nova linha? 2017-12-20 05:48:21 +0800 CST

Hot tag

linux bash debian shell-script text-processing ubuntu centos shell awk ssh

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve