Questão Saída de piping de 'find' para 'xargs wc' dá totais não razoáveis


Em um projeto com milhares de arquivos, eu queria comparar o total de linhas de código, para linhas de código apenas em PHP (descartando CSS, JavaScript, etc.).

Quando eu corro

find . -type f | xargs wc -l

o total na última linha é mais baixo do que quando corro

find -E . -regex '.+\.(php|inc)' -type f | xargs wc -l

Considerando o segundo find tem que ser uma lista menor de arquivos do que (é um subconjunto estrito de) o primeiro find, como pôde wc reportar um total maior no segundo caso?


0


origem




Respostas:


xargs só pode passar ARG_MAX bytes de argumentos para wc.

No meu Mac, o ARG_MAX é menor que os nomes completos dos arquivos e os caminhos relativos dos arquivos do projeto inteiro, portanto, primeiro comando, xargs despejou os resultados do find para wc  em dois lotes, o que significa que wc apagar dois totais, cercado por milhares de nomes de arquivos. Mas ARG_MAX passou a ser maior que o segundo  find saída, então o segundo, menor encontrar tudo mostrou em 1  wc total.

A correção foi usar esses comandos, para que eu pudesse ver todos os totais sem as linhas de contagem de arquivos individuais (chatas):

find . -type f | xargs wc -l | grep total
find -E . -regex '.+\.(php|inc)' -type f | xargs wc -l | grep total

Em seguida, some as várias linhas "totais" manualmente.


1



Ou fazer find . -type f | xargs cat | wc -l - Alan Shutko


Existem muitas maneiras de fazer isso e xargs não é o melhor. Aqui está um casal:

  1. O mais simples é cat cada um dos arquivos encontrados por find e conte as linhas. Cuidado, isso só funciona com seus nomes de arquivos sem espaços ou caracteres estranhos:

    find . -type f | while read n; do cat $n; done | wc -l
    find -E . -regex '.+\.(php|inc)' -type f | while read n; do cat $n; done | wc -l 
    

    Se os nomes dos seus arquivos provavelmente contiverem caracteres estranhos (barras, espaços, etc.) use isto:

    find . -type f | while IFS= read -r n; do cat $n; done | wc -l
    find -E . -regex '.+\.(php|inc)' -type f | while IFS= read -r n; do cat $n; done | wc -l 
    
  2. Uma maneira melhor é usar o find -exec opção:

    find . -name "*.pep" -exec cat {} \; | wc
    find -E . -regex '.+\.(php|inc)' -type f -exec cat {} \; | wc
    

0



Dado que o problema real de Jeremy era que ele tinha muitos nomes de arquivos para caber em uma linha de comando, é improvável que sua primeira abordagem funcionasse. - Scott
@Scott sim, bom ponto, responda atualizado. - terdon
Pode até IFS= read –r n lidar com nomes de arquivos contendo novas linhas? Talvez a solução ideal seja um híbrido: find … –print0 | xargs –0 cat | wc. - Scott
Maldito @Scott, bom ponto novamente :). Eu pensamento que poderia lidar com novas linhas por causa de um comentário de um usuário muito experiente da UL.SE, que agora percebo que devo ter entendido mal. -print0 também falha em novas linhas. Eu removi a referência para novas linhas para evitar confusão. - terdon
Sim, mas não foi isso que eu sugeri. o –0 opção de xargs e a –print0 predicado de find foram projetados para trabalhar de mãos dadas. - Scott


Usar awk para resumir os vários números "totais" do wc -l saídas!

(Nota: wc -l retorna o número de caracteres de nova linha, i. e. "linhas" finais sem uma final \n personagem não será contado - como é o caso com awk ou sed.)

export LC_ALL=C
find . -type f -print0 | xargs -0 wc -l | 
    awk '/^ *[[:digit:]]+ total$/{ total+=$1 }END{print total}'


# xargs alternatives using: find ... -exec <wc|awk|sed> ... '{}' +
#man find | less -p '{} \+'

# wc
find . -type f -exec wc -l '{}' + 2>/dev/null | 
   awk '/^ *[[:digit:]]+ total$/{ total+=$1 }END{print total}'

# awk
find . -type f -exec awk 'END {print NR}' '{}' + 2>/dev/null | 
    awk '{ total+=$1 }END{print total}'

# sed
find . -type f -exec sed -n '$=' '{}' + 2>/dev/null | 
    awk '{ total+=$1 }END{print total}'

0