Contar o número de arquivos que não são links simbólicos na saída de “locate”

0

Estou tentando contar o número de arquivos vinculados não simbólicos passados pelo comando locate. Eu tentei várias opções e acho que isso é o mais promissor:

locate -r "$PWD.*\.c$" | xargs -0 -I{} test -f {} && echo "regular file" | wc -l

problema é que não funciona.

Existem 30 arquivos no total, 1 é um link simbólico, então wc -l deve ser 29 .

Eu tentei ignorar xargs ao todo:

locate -r "$PWD.*\.c$" | test -f && echo "regular file" | wc -l

Eu tentei o link simbólico não :

locate -r "$PWD.*\.c$" | test ! -h && echo "regular file" | wc -l
locate -r "$PWD.*\.c$" | test ! -L && echo "regular file" | wc -l

Qual é a maneira mais eficiente de canalizar locate output e contar o número de arquivos regulares vs. links simbólicos?

Responder aos comentários

Algumas pessoas adoram o comando locate e algumas pessoas adoram o comando find . Espero ser agnóstico, mas gosto de usar locate quando possível. Comentários foram postados e gostaria de respondê-los aqui na pergunta:

  • updatedb leva 30 segundos para ser executado na primeira vez, mas apenas 4 segundos nos tempos subseqüentes. Ter cron rodando a cada 5 minutos era uma reação instintiva a apenas uma falha por dia. Mas o laptop só executa de 10 a 20% do uso da CPU e nunca há nenhum atraso.
  • Depois de limpar os caches find demora 1 minuto e 9 segundos para encontrar um único arquivo.
  • Depois de limpar os caches locate demora 1 segundo para encontrar o mesmo arquivo.

Aqui estão alguns benchmarks que você pode duplicar no seu sistema:

$ sudo -i
# sync; echo 1 > /proc/sys/vm/drop_caches; sync; echo 2 > /proc/sys/vm/drop_caches; sync; echo 3 > /proc/sys/vm/drop_caches; exit
logout

$ time locate .hidden.c | wc -l
1

real    0m0.790s
user    0m0.758s
sys     0m0.028s

$ sudo -i
# sync; echo 1 > /proc/sys/vm/drop_caches; sync; echo 2 > /proc/sys/vm/drop_caches; sync; echo 3 > /proc/sys/vm/drop_caches; exit
logout

$ time find / iname '.hidden.c'  2>/dev/null | wc -l
1888926

real    1m9.044s
user    0m5.158s
sys     0m15.004s

$ sudo -i
# sync; echo 1 > /proc/sys/vm/drop_caches; sync; echo 2 > /proc/sys/vm/drop_caches; sync; echo 3 > /proc/sys/vm/drop_caches; exit
logout

$ time sudo updatedb

real    0m29.323s
user    0m1.267s
sys     0m4.784s

$ time sudo updatedb

real    0m3.592s
user    0m0.479s
sys     0m1.211s

find é inquestionavelmente muito mais robusto que locate , mas locate é muitas vezes mais rápido e tem uma sintaxe mais fácil de lembrar.

É verdade que você precisa lembrar de executar sudo updatedb ou de uma vez passar o argumento -u para o comando locate para atualizar o banco de dados para incluir arquivos criados hoje ou excluir arquivos excluídos hoje. Mas por outro lado, com find , você precisa lembrar de passar o parâmetro 2>/dev/null .

Eu optei por ter cron run updatedb a cada cinco minutos porque meu laptop é mal-trabalhado em primeiro lugar e eu sou preguiçoso em segundo lugar.

    
por WinEunuuchs2Unix 12.04.2018 / 03:08

4 respostas

2

Este comando chegou perto:

locate -r "$PWD.*\.c$" | xargs -0 -I{} test -f {} && echo "regular file" | wc -l

Problemas:

  • Você está usando uma entrada delimitada nul com xargs, mas locate não está fornecendo uma saída delimitada nula.
  • O && echo é executado para o canal locate | xargs como um todo e não para as execuções individuais de test

Tente:

locate -0r "$PWD.*\.c$" | xargs -0 -I{} sh -c 'test -f "$1" && echo "regular file"' _  {} | wc -l
  • habilitando a saída delimitada nul por locate com -0
  • combinando test e echo (isso pode ser melhorado com um loop sobre argumentos em sh -c , para que vários arquivos sejam processados por invocação de sh )

Ainda há o problema de operadores regex aparecendo em $PWD .

    
por 12.04.2018 / 03:31
2

com zsh :

set -o extendedglob # best in ~/.zshrc
c_regular_files=(
  ${(0)^"$(locate -0 "${${PWD%/}//(#m)[]\*?]/\$MATCH}/*.c")"}(N.)
)
echo there are at least $#c_regular_files regular files whose name ends in .c
  • Precisamos escapar do [ , ? , \ e * in $PWD para locate para não interpretá-los como operadores de caractere curinga (seria ainda pior com -r para regexps que tem mais operadores incluindo . , comum em nomes de arquivos)
  • $PWD == / deve ser tratado especialmente; com $PWD em vez de ${PWD%/} , estaríamos executando locate -0 "//*.c" , o que não retornaria nada.
  • -0 para arquivos a serem delimitados por NUL (a nova linha não funcionaria como é permitido em caminhos de arquivos).
  • . é para o arquivo regular . Ao contrário de [ -f , isso exclui links simbólicos para o arquivo regular. Se você quiser que cada arquivo não-symlink .c (permitindo qualquer outro tipo de arquivo, como diretório, fifo, socket ...), substitua . por ^@ .

Em qualquer caso, observe que locate retorna uma lista com base na última vez em que o banco de dados locate foi atualizado, o que pode não refletir a realidade atual.

    
por 20.04.2018 / 11:03
2

Em vez de analisar a saída de locate (que é frágil e pode perder itens que foram alterados desde a última atualização do banco de dados ou que não estão disponíveis para todos usuários), use find .

A seguir, você encontrará todos os arquivos .c no diretório atual que são arquivos regulares (não links simbólicos):

find . -type f -name '*.c'

Dada a estrutura de diretórios

.
|-- file-a.c
|-- file-b.c
|-- file-c.c
|-- file-d.c
|-- link-b.c -> file-b.c
'-- link-d.c -> file-d.c

Isso retornaria

./file-a.c
./file-b.c
./file-c.c
./file-d.c

Para contá-los:

find . -type f -name '*.c' | wc -l

ou, se você tiver nomes de arquivos com novas linhas em seus nomes,

find .//. -name '*.c' -type f | grep -c //

Fazer o mesmo para links simbólicos envolveria alterar o -type f para -type l .

    
por 12.04.2018 / 08:34
1

Com o GNU Parallel, é assim:

locate -r "$PWD.*\.c$" | parallel 'test -f {} && echo "regular file"' | wc -l

Como você pode ver, é extremamente próximo da sua tentativa inicial.

Se você tem menos de 100 acessos, você pode usar os conjuntos GNU Parallel $? para o número de trabalhos com falha até 100 (ISTO NÃO ESCALA):

ls  *txt | parallel \! test -f {}
echo $?

Se você precisar ainda mais rápido:

locate -r "$PWD.*\.c$" |
  perl -ne 'chomp; -l $_ or $s+= -f $_; END{print "$s\n"}'

Ou a combinação:

locate -r "$PWD.*\.c$" |
  parallel --block 10k --pipe -q perl -ne 'chomp; -l $_ or $s+= -f $_; END{print "$s\n"}' |
  awk '{s+=$1} END {print s}'
    
por 19.04.2018 / 22:16