Preciso inspecionar todos os subdiretórios e relatar quantos arquivos (sem mais recursões) eles contêm:
directoryName1 numberOfFiles
directoryName2 numberOfFiles
Preciso inspecionar todos os subdiretórios e relatar quantos arquivos (sem mais recursões) eles contêm:
directoryName1 numberOfFiles
directoryName2 numberOfFiles
Respostas:
Isso é feito de maneira segura e portátil. Não ficará confuso com nomes de arquivos estranhos.
for f in *; do [ -d ./"$f" ] && find ./"$f" -maxdepth 1 -exec echo \; | wc -l && echo $f; done
Observe que ele imprimirá o número de arquivos primeiro, depois o nome do diretório em uma linha separada. Se você deseja manter o formato do OP, precisará de mais formatação, por exemplo
for f in *; do [ -d ./"$f" ] && find ./"$f" -maxdepth 1 -exec echo \;|wc -l|tr '\n' ' ' && echo $f; done|awk '{print $2"\t"$1}'
Se você tiver um conjunto específico de subdiretórios de seu interesse, poderá substituí *
-lo por eles.
Por que isso é seguro? (e, portanto, digno de script)
Os nomes de arquivos podem conter qualquer caractere, exceto /
. Existem alguns caracteres que são tratados especialmente pelo shell ou pelos comandos. Isso inclui espaços, novas linhas e traços.
Usar a for f in *
construção é uma maneira segura de obter cada nome de arquivo, não importa o que ele contenha.
Depois de ter o nome do arquivo em uma variável, você ainda precisa evitar coisas como find $f
. Se $f
continha o nome do arquivo -test
, find
reclamaria da opção que você acabou de dar. A maneira de evitar isso é usando ./
na frente do nome; dessa forma, ele tem o mesmo significado, mas não começa mais com um traço.
Novas linhas e espaços também são um problema. Se $f
contiver "olá, amigo" como um nome de arquivo,, find ./$f
é find ./hello, buddy
. Você está dizendo find
para olhar ./hello,
e buddy
. Se esses não existirem, ele irá reclamar e nunca irá investigar ./hello, buddy
. Isso é fácil de evitar - use aspas em torno de suas variáveis.
Por fim, os nomes de arquivos podem conter novas linhas, portanto, contar novas linhas em uma lista de nomes de arquivos não funcionará; você receberá uma contagem extra para cada nome de arquivo com uma nova linha. Para evitar isso, não conte novas linhas em uma lista de arquivos; em vez disso, conte novas linhas (ou qualquer outro caractere) que representem um único arquivo. É por isso que o find
comando tem simplesmente -exec echo \;
e não -exec echo {} \;
. Eu só quero imprimir uma única nova linha com o objetivo de calcular os arquivos.
-mindepth 1
-printf '\n'
vez de -exec echo
.
-printf
, mas não se quiser que ele funcione no FreeBSD, por exemplo.
Supondo que você esteja procurando uma solução Linux padrão, uma maneira relativamente direta de conseguir isso é find
:
find dir1/ dir2/ -maxdepth 1 -type f | wc -l
Onde find
atravessa os dois subdiretórios especificados, para um -maxdepth
de 1, o que evita recursões adicionais e apenas relata arquivos ( -type f
) separados por novas linhas. O resultado é então canalizado wc
para contar o número dessas linhas.
find . -maxdepth 1 -type d
saída?
find $dirs ...
ou, (b) se forem exclusivamente no diretório um nível mais elevado, glob a partir desse diretório,find */ ...
-exec echo
ao seu comando find - dessa forma, não ecoará o nome do arquivo, apenas uma nova linha.
Por "sem recursão", você quer dizer que se directoryName1
possui subdiretórios, não deseja contar os arquivos nos subdiretórios? Nesse caso, aqui está uma maneira de contar todos os arquivos regulares nos diretórios indicados:
count=0
for d in directoryName1 directoryName2; do
for f in "$d"/* "$d"/.[!.]* "$d"/..?*; do
if [ -f "$f" ]; then count=$((count+1)); fi
done
done
Observe que o -f
teste executa duas funções: testa se a entrada correspondente a um dos globs acima é um arquivo regular e testa se a entrada foi uma correspondência (se um dos globs não corresponder a nada, o padrão permanecerá como ¹). Se você deseja contar todas as entradas nos diretórios fornecidos, independentemente do tipo, substitua -f
por -e
.
O Ksh tem uma maneira de fazer com que os padrões correspondam aos arquivos de ponto e produzir uma lista vazia, caso nenhum arquivo corresponda a um padrão. Portanto, no ksh, você pode contar arquivos regulares como este:
FIGNORE='.?(.)'
count=0
for x in ~(N)directoryName1/* ~(N)directoryName2/*; do
if [ -f "$x" ]; then ((++count)); fi
done
ou todos os arquivos simplesmente assim:
FIGNORE='.?(.)'
files=(~(N)directoryName1/* ~(N)directoryName2/*)
count=${#files}
O Bash tem maneiras diferentes de simplificar isso. Para contar arquivos regulares:
shopt -s dotglob nullglob
count=0
for x in directoryName1/* directoryName2/*; do
if [ -f "$x" ]; then ((++count)); fi
done
Para contar todos os arquivos:
shopt -s dotglob nullglob
files=(directoryName1/* directoryName2/*)
count=${#files}
Como de costume, é ainda mais simples no zsh. Para contar arquivos regulares:
files=({directoryName1,directoryName2}/*(DN.))
count=$#files
Mude (DN.)
para (DN)
para contar todos os arquivos.
¹ Observe que cada padrão corresponde a si próprio; caso contrário, os resultados podem estar desativados (por exemplo, se você estiver contando arquivos que começam com um dígito, você não pode simplesmente fazer isso for x in [0-9]*; do if [ -f "$x" ]; then …
porque pode haver um arquivo chamado [0-9]foo
).
Com base em um script de contagem , a resposta de Shawn e um truque do Bash para garantir que mesmo nomes de arquivos com novas linhas sejam impressos em um formulário utilizável em uma única linha:
for f in *
do
if [ -d "./$f" ]
then
printf %q "$f"
printf %s ' '
find "$f" -maxdepth 1 -printf x | wc -c
fi
done
printf %q
é imprimir uma versão entre aspas de uma string, ou seja, uma string de linha única que você pode colocar em um script Bash para ser interpretada como uma string literal, incluindo (potencialmente) novas linhas e outros caracteres especiais. Por exemplo, veja echo -n $'\tfoo\nbar'
vs printf %q $'\tfoo\nbar'
.
O find
comando funciona simplesmente imprimindo um único caractere para cada arquivo e depois contando-os em vez de contar as linhas.
Aqui está uma "força-bruta" maneira -ish para obter o seu resultado, usando find
, echo
, ls
, wc
, xargs
e awk
.
find . -maxdepth 1 -type d -exec sh -c "echo '{}'; ls -1 '{}' | wc -l" \; | xargs -n 2 | awk '{print $1" "$2}'
for i in *; do echo $i; ls $i | wc -l; done
for i in `ls -1`; do echo $i : `ls -1 $i|wc -l`; done
find
quando o Bash fará?(shopt -s dotglob; for dir in */; do all=("$dir"/*); echo "$dir: ${#all[@]}"; done)
: Para todos os diretórios, contar o número de entradas no diretório (incluindo arquivos de pontos ocultos, excluindo.
e..
)