Ao contar arquivos de origem e LOC com locate e find - por que os arquivos Python são diferentes?

2

Estou tendo problemas para entender por que find e locate funcionariam de maneira diferente para arquivos de origem C e Python. Meu objetivo é contar os arquivos de origem numéricos e a soma de suas linhas de código-fonte para um determinado idioma. Usei find e locate para comparar as saídas ( updatedb foi executado antes disso com sudo para garantir que locate relata os resultados atuais).

Para arquivos C, isso funciona como esperado, o número de arquivos de origem é o mesmo

$ find / -name *.c |& grep -v "Permission denied" | wc -l
1056
$ locate *.c | wc -l
1056

Usando xargs , a soma das linhas de código-fonte também é a mesma.

$ locate *.c | xargs wc -l | tail -3
     138 /usr/src/kernels/3.10.0-693.el7.ppc64/scripts/selinux/genheaders/genheaders.c
     147 /usr/src/kernels/3.10.0-693.el7.ppc64/scripts/selinux/mdp/mdp.c
  705376 total

$ find / -name *.c |& grep -v "Permission denied" | xargs wc -l | tail -3
    2994 /opt/Python-3.6.2/Objects/listobject.c
     821 /opt/Python-3.6.2/Objects/bytes_methods.c
  705376 total

Só para testar, isso também funciona para arquivos com extensão .java - obtenho os mesmos resultados consistentes. No entanto, quando eu repito o mesmo com arquivos Python (ou seja, .py extension)

Número do arquivo de origem corresponde.

$ find / -name *.py |& grep -v "Permission denied" | wc -l
9249
$ locate *.py | wc -l
9249

Mas a soma das linhas de código dos arquivos Python fornece resultados muito diferentes.

$ locate *.py | xargs wc -l | tail -3
wc: /usr/lib/python2.7/site-packages/setuptools/script: No such file or directory
wc: template: No such file or directory
wc: (dev).py: No such file or directory
wc: /usr/lib/python2.7/site-packages/setuptools/script: No such file or directory
wc: template.py: No such file or directory
     220 /usr/src/kernels/3.10.0-693.el7.ppc64/scripts/rt-tester/rt-tester.py
     129 /usr/src/kernels/3.10.0-693.el7.ppc64/scripts/tracing/draw_functrace.py
  753350 total

$ find / -name *.py |& grep -v "Permission denied" | xargs wc -l | tail -3
wc: /usr/lib/python2.7/site-packages/setuptools/script: No such file or directory
wc: template: No such file or directory
wc: (dev).py: No such file or directory
wc: /usr/lib/python2.7/site-packages/setuptools/script: No such file or directory
wc: template.py: No such file or directory
    1919 /opt/Python-3.6.2/python-gdb.py
      69 /opt/Python-3.6.2/python-config.py
 1034101 total

Alguém pode explicar porque é esse o caso? O que há de tão diferente em arquivos Python (eu não posso realmente acreditar que isso tenha a ver com o tipo de arquivo, mas estou perplexo). O que estou perdendo aqui?

Mesmos resultados ímpares no Ubuntu e no RH

Eu corro updatedb com sudo , mas estou executando todos esses comandos como um usuário comum.

    
por Levon 28.10.2017 / 01:35

1 resposta

2

Existem muitos problemas com seus comandos.

Primeiro, locate *.c só procura arquivos que correspondam *.c se você executá-lo em um diretório que não contenha nenhum arquivo cujo nome corresponda a *.c . Caso contrário, o shell expande *.c para a lista de arquivos correspondentes. Isso provavelmente não está acontecendo, caso contrário, você obteria menos correspondências, mas deixar globs sem aspas como essa é um mau hábito, porque irá morder você um dia. (É um tópico frequente neste site.) O mesmo vale para find -name *.c . Em vez disso, escreva

locate '*.c' …
find / -name '*.c' …

ou algo similar.

Existem alguns motivos pelos quais locate e find podem gerar resultados diferentes. Eles não parecem se aplicar no seu caso, já que você está recebendo o mesmo número de acessos, mas, mais uma vez, isso é algo que você precisa saber.

  • Os resultados de locate são armazenados em cache da última execução de updatedb . Isso geralmente é executado uma vez à noite. find resultados calculados cada vez que você executar o comando.
  • Dependendo do sistema, em qual locate implementação você e como ela está configurada, ela pode permitir que você veja somente arquivos acessíveis publicamente (por exemplo, findutils GNU, em vez de mlocate ou slocate), ou pode fazer uma aproximação de os arquivos que você tem permissão para acessar (por exemplo, porque há uma configuração complexa envolvendo módulos de segurança do Linux que distinguem entre aplicativos que tentam acessar o arquivo).
  • O padrão *SUFFIX significa a mesma coisa para locate e para find -name (supondo que SUFFIX não contenha barras ou curingas), mas outros padrões não. Por exemplo, locate foo é equivalente a find / -name '*foo*' , não a find / -name 'foo' .

Outra coisa que pode, mas provavelmente não causa, problemas é que você canalizou mensagens de erro de find para a parte de processamento de dados do seu comando. Você remove linhas contendo Permission denied , o que faz com que você perca arquivos que contenham isso como parte de seu nome (ok, provavelmente você não possui nenhum) e faz com que qualquer mensagem de erro que não contenha Permission denied seja interpretada como uma linha de entrada. Raramente é uma boa ideia misturar a saída de dados com a saída de erro, e é um absurdo aqui. Se você quiser ignorar os erros, redirecione-os para /dev/null :

find … 2>/dev/null | …

O que definitivamente está mordendo você é que xargs espera uma sintaxe de entrada diferente do que o find produz. Na entrada de xargs , qualquer espaço em branco separa itens, não apenas quebras de linha. Os três caracteres \'" também são analisados especialmente. Espaços são comuns em nomes de arquivos e todos os outros caracteres são permitidos além de / e de bytes nulos. Uma das linhas que xargs recebe como entrada é

/usr/lib/python2.7/site-packages/setuptools/script template (dev).py

Para xargs , são três itens: /usr/lib/python2.7/site-packages/setuptools/script , template e (dev).py . O motivo das mensagens de erro de wc agora deve estar claro.

Existem várias soluções para isso. Uma é usar o formato delimitado por nulo para find e xargs . Isso funciona com qualquer nome de arquivo, até mesmo nomes de arquivos contendo novas linhas (que são permitidas, mas incomuns).

find / -name '*.py' -print0 | xargs -0 wc -l | tail -3

Outra é esquecer a problemática xargs e fazer find invocar o comando diretamente.

find / -name '*.py' -exec wc -l {} + | tail -3

A primeira solução pode ser aplicável à sua implementação locate , verifique se ela possui uma opção -0 . A segunda solução é específica para find . Se você estiver preso à saída delimitada por nova linha de locate e tiver a versão GNU de xargs , poderá usar -d '\n' para fazer com que ela analise a entrada como delimitada por nova linha sem qualquer forma de cotação. / p>

locate '*.py' | xargs -d '\n' wc -l | tail -3

Esse foi o seu principal problema. Um problema adicional é que há um comprimento máximo para a linha de comando. O comando xargs (ou a ação -exec … {} + de find ) coloca quantos nomes de arquivo puder em uma linha de comando, e se eles não couberem todos, então o comando (aqui, wc -l ) é executado várias vezes, uma vez para cada lote de arquivos. Com tail -3 , você só verá os dois últimos arquivos e o total do último lote (supondo que haja pelo menos dois arquivos no último lote). Os arquivos nos lotes anteriores não são refletidos nessa saída. Como find e locate podem não relatar arquivos na mesma ordem, você pode ver resultados diferentes.

Como resolver o problema de tamanho máximo depende do que você deseja fazer com os dados. Se tudo o que você quer é o total geral, então uma maneira (assumindo que não há novas linhas nos nomes dos arquivos) é contar todas as linhas total .

… | xargs -d '\n' wc -l | awk '/^[0-9]+\ttotal$/ {total += $1} END {print total}'
    
por 28.10.2017 / 02:13