Respostas:
Este simples liner deve funcionar em qualquer shell, não apenas no bash:
ls -1q log* | wc -l
ls -1q fornecerá uma linha por arquivo, mesmo que contenham espaços em branco ou caracteres especiais, como novas linhas.
A saída é canalizada para wc -l, que conta o número de linhas.
ls, pois cria um processo filho. log*é expandido pelo shell, não ls, então um simples echoseria.
logsno diretório em questão, o conteúdo desse diretório de logs também será contado. Provavelmente isso não é intencional.
Você pode fazer isso com segurança (ou seja, não será incomodado por arquivos com espaços ou \nem seus nomes) com o bash:
$ shopt -s nullglob
$ logfiles=(*.log)
$ echo ${#logfiles[@]}
É necessário ativar nullglobpara que você não obtenha o literal *.logna $logfiles matriz se nenhum arquivo corresponder. (Consulte Como "desfazer" um 'set -x'? Para exemplos de como redefini-lo com segurança.)
shopt -u nullglobdeve ser ignorada se nullglobnão estiver definida, então você começou.
*.logpor apenas *contará diretórios. Se os arquivos que você deseja enumerar tiverem a convenção de nomenclatura tradicional name.extension, use *.*.
Muitas respostas aqui, mas algumas não levam em consideração
-l)*.logvez delog*logsque corresponde log*)Aqui está uma solução que lida com todos eles:
ls 2>/dev/null -Ubad1 -- log* | wc -l
Explicação:
-Ufaz lscom que não classifique as entradas, o que significa que não precisa carregar toda a lista de diretórios na memória-bimprime escapes no estilo C para caracteres não gráficos, fazendo com que novas linhas sejam impressas como \n.-aimprime todos os arquivos, mesmo arquivos ocultos (não é estritamente necessário quando a glob log*não implica arquivos ocultos)-dimprime diretórios sem tentar listar o conteúdo do diretório, o que lsnormalmente faria-1 garante que ele esteja em uma coluna (o ls faz isso automaticamente ao gravar em um pipe, por isso não é estritamente necessário)2>/dev/nullredireciona o stderr para que, se houver 0 arquivos de log, ignore a mensagem de erro. (Observe que isso shopt -s nullglobfaria com lsque listasse todo o diretório de trabalho.)wc -lconsome a listagem de diretórios enquanto ela está sendo gerada, portanto a saída de lsnunca fica na memória em nenhum momento.--Os nomes dos arquivos são separados do comando usando-os --para não serem entendidos como argumentos para ls(caso log*seja removido)O shell se expandirá log*para a lista completa de arquivos, o que pode esgotar a memória se houver muitos arquivos; portanto, executá-lo no grep é melhor:
ls -Uba1 | grep ^log | wc -l
Este último lida com diretórios extremamente grandes de arquivos sem usar muita memória (embora use um subshell). O -dnão é mais necessário, porque está listando apenas o conteúdo do diretório atual.
Para uma pesquisa recursiva:
find . -type f -name '*.log' -printf x | wc -c
wc -ccontará o número de caracteres na saída de find, enquanto -printf xinforma findpara imprimir um único xpara cada resultado.
Para uma pesquisa não recursiva, faça o seguinte:
find . -maxdepth 1 -type f -name '*.log' -printf x | wc -c
-name '*.log', ele contará todos os arquivos, que é o que eu precisava para o meu caso de uso. Além disso, o sinalizador -maxdepth é extremamente útil, obrigado!
find; basta imprimir algo diferente do nome do arquivo literal.
A resposta aceita para esta pergunta está errada, mas eu tenho um representante baixo, portanto não consigo adicionar um comentário.
A resposta correta para esta pergunta é dada por Mat:
shopt -s nullglob
logfiles=(*.log)
echo ${#logfiles[@]}
O problema com a resposta aceita é que wc -l conta o número de caracteres de nova linha e os conta mesmo se eles imprimirem no terminal como '?' na saída de 'ls -l'. Isso significa que a resposta aceita FALHA quando um nome de arquivo contém um caractere de nova linha. Eu testei o comando sugerido:
ls -l log* | wc -l
e informa erroneamente um valor 2, mesmo que exista apenas 1 arquivo correspondente ao padrão cujo nome contenha um caractere de nova linha. Por exemplo:
touch log$'\n'def
ls log* -l | wc -l
Se você possui muitos arquivos e não deseja usar a shopt -s nullglobsolução elegante e básica, pode usar find e assim por diante, desde que não imprima o nome do arquivo (que pode conter novas linhas).
find -maxdepth 1 -name "log*" -not -name ".*" -printf '%i\n' | wc -l
Ele encontrará todos os arquivos que correspondem ao log * e que não começam com .*- O "não nome. *" É redunante, mas é importante observar que o padrão para "ls" é não mostrar arquivos de ponto, mas o padrão pois encontrar é incluí-los.
Esta é uma resposta correta e lida com qualquer tipo de nome de arquivo que você possa fornecer, porque o nome do arquivo nunca é passado entre os comandos.
Mas, a shopt nullglobresposta é a melhor resposta!
findvs usar lssão duas maneiras diferentes de resolver o problema. findnem sempre está presente em uma máquina, mas lsnormalmente é,
findprovavelmente também não tem todas essas opções sofisticadas ls.
-maxdepth 1
findfaz isso por padrão. Isso pode criar confusão se você não perceber que há uma pasta filho oculta e pode tornar vantajoso o uso lsem algumas circunstâncias, que não relatam arquivos ocultos por padrão.
Aqui está o meu forro para isso.
file_count=$( shopt -s nullglob ; set -- $directory_to_search_inside/* ; echo $#)
set -- não está fazendo nada, exceto nos preparando $#, que armazena o número de argumentos da linha de comando que foram passados para o programa shell
(reputação insuficiente para comentar)
Este é BUGGY :
ls -1q some_pattern | wc -l
Se shopt -s nullglobestiver definido, ele imprime o número de TODOS os arquivos regulares, não apenas aqueles com o padrão (testado no CentOS-8 e Cygwin). Quem sabe o que outros bugs sem sentido lstêm?
Isso é CORRETO e muito mais rápido:
shopt -s nullglob; files=(some_pattern); echo ${#files[@]};
Faz o trabalho esperado.
0.006no CentOS e 0.083no Cygwin (caso seja usado com cuidado).
0.000no CentOS e 0.003no Cygwin.
Você pode definir esse comando facilmente, usando uma função shell. Este método não requer nenhum programa externo e não gera nenhum processo filho. Ele não tenta a lsanálise perigosa e manipula caracteres “especiais” (espaços em branco, novas linhas, barras invertidas e assim por diante). Ele depende apenas do mecanismo de expansão de nome de arquivo fornecido pelo shell. É compatível com pelo menos sh, bash e zsh.
A linha abaixo define uma função chamada countque imprime o número de argumentos com os quais foi chamada.
count() { echo $#; }
Basta chamá-lo com o padrão desejado:
count log*
Para que o resultado seja correto quando o padrão de globbing não corresponder, a opção de shell nullglob(ou failglob- que é o comportamento padrão no zsh) deve ser configurada no momento em que a expansão ocorrer. Pode ser definido assim:
shopt -s nullglob # for sh / bash
setopt nullglob # for zsh
Dependendo do que você deseja contar, você também pode estar interessado na opção de shell dotglob.
Infelizmente, pelo menos com o bash, não é fácil definir essas opções localmente. Se você não deseja defini-los globalmente, a solução mais direta é usar a função desta maneira mais complicada:
( shopt -s nullglob ; shopt -u failglob ; count log* )
Se você deseja recuperar a sintaxe leve count log*, ou se realmente deseja evitar a geração de um subshell, pode hackear algo como:
# sh / bash:
# the alias is expanded before the globbing pattern, so we
# can set required options before the globbing gets expanded,
# and restore them afterwards.
count() {
eval "$_count_saved_shopts"
unset _count_saved_shopts
echo $#
}
alias count='
_count_saved_shopts="$(shopt -p nullglob failglob)"
shopt -s nullglob
shopt -u failglob
count'
Como um bônus, esta função é de uso mais geral. Por exemplo:
count a* b* # count files which match either a* or b*
count $(jobs -ps) # count stopped jobs (sh / bash)
Ao transformar a função em um arquivo de script (ou um programa C equivalente), que pode ser chamado pelo PATH, também pode ser composta por programas como finde xargs:
find "$FIND_OPTIONS" -exec count {} \+ # count results of a search
Pensei bastante nessa resposta, especialmente considerando as coisas que não analisamos . No começo, eu tentei
<AVISO! NÃO FUNCIONA>
du --inodes --files0-from=<(find . -maxdepth 1 -type f -print0) | awk '{sum+=int($1)}END{print sum}'
</ ATENÇÃO! NÃO FUNCIONA>
que funcionava se houvesse apenas um nome de arquivo como
touch $'w\nlf.aa'
mas falhei se eu fizesse um nome de arquivo como este
touch $'firstline\n3 and some other\n1\n2\texciting\n86stuff.jpg'
Eu finalmente inventei o que estou colocando abaixo. Observe que eu estava tentando obter uma contagem de todos os arquivos no diretório (sem incluir nenhum subdiretório). Acho que, junto com as respostas de @Mat e @Dan_Yard, além de ter pelo menos a maioria dos requisitos estabelecidos por @mogsie (não tenho certeza sobre a memória). Acho que a resposta de @mogsie está correta, mas sempre tento me afastar da análise, a lsmenos que seja uma situação extremamente específica.
awk -F"\0" '{print NF-1}' < <(find . -maxdepth 1 -type f -print0) | awk '{sum+=$1}END{print sum}'
Mais facilmente:
awk -F"\0" '{print NF-1}' < \
<(find . -maxdepth 1 -type f -print0) | \
awk '{sum+=$1}END{print sum}'
Isso é uma descoberta específica para arquivos, delimitando a saída com um caractere nulo (para evitar problemas com espaços e alimentações de linha) e depois contando o número de caracteres nulos. O número de arquivos será um a menos que o número de caracteres nulos, pois haverá um caractere nulo no final.
Para responder à pergunta do OP, há dois casos a considerar
1) Pesquisa não recursiva:
awk -F"\0" '{print NF-1}' < \
<(find . -maxdepth 1 -type f -name "log*" -print0) | \
awk '{sum+=$1}END{print sum}'
2) Pesquisa recursiva. Observe que o conteúdo do -nameparâmetro pode precisar ser alterado para um comportamento ligeiramente diferente (arquivos ocultos etc.).
awk -F"\0" '{print NF-1}' < \
<(find . -type f -name "log*" -print0) | \
awk '{sum+=$1}END{print sum}'
Se alguém quiser comentar sobre como essas respostas se comparam às que eu mencionei nesta resposta, faça.
Observe que cheguei a esse processo de pensamento ao obter esta resposta .
Aqui está o que eu sempre faço:
ls log * | awk 'END {print NR}'
awk 'END{print NR}'deve ser equivalente a wc -l.
ls -1 log* | wc -l
O que significa listar um arquivo por linha e canalizá-lo para o comando de contagem de palavras com a alternância de parâmetros para as linhas de contagem.
-l, uma vez que isso exigestat(2)em cada arquivo e, para fins de contagem, não acrescenta nada.