Eu não examinei a saída com links simbólicos, mas:
find . -type f -iname '*.c' -printf '%hshopt -s globstar dotglob nocaseglob
printf "%q\n" **/*.c | awk -F/ '{NF--; f++} !c[$0]++{d++} END {print f, d}'
' |
sort -z |
uniq -zc |
sed -zr 's/([0-9]) .*/ 1/' |
tr '$ shopt -s globstar dotglob nocaseglob
$ printf "%q\n" ./**/*.c | awk -F/ '{NF--; f++} !c[$0]++{d++} END {print f, d}'
34 15
$ echo $BASH_VERSION
4.4.18(1)-release
' '\n' |
awk '{f += $1; d += $2} END {print f, d}'
- O comando
find
imprime o nome do diretório de cada arquivo.c
encontrado. -
sort | uniq -c
nos dará quantos arquivos estão em cada diretório (osort
pode ser desnecessário aqui, não tenho certeza) - com
sed
, substituo o nome do diretório por1
, eliminando assim todos os possíveis caracteres estranhos, com apenas a contagem e1
restantes - permitindo que eu converta para saída separada por nova linha com
tr
- que então eu so passo com o awk, para obter o número total de arquivos e o número de diretórios que continham esses arquivos. Note que
d
aqui é essencialmente o mesmo queNR
. Eu poderia ter omitido a inserção de1
no comandosed
, e apenas imprimiNR
aqui, mas acho que isso é um pouco mais claro.
Até tr
, os dados são delimitados por NUL, seguros contra todos os nomes de arquivos válidos.
Com zsh e bash, você pode usar printf %q
para obter uma string entre aspas, que não teria novas linhas nela. Então, você pode fazer algo como:
$ printf "%q\n" ./**/*.c(D.:h) | awk '!c[$0]++ {d++} END {print NR, d}'
29 7
No entanto, embora **
não deva expandir para links simbólicos para diretórios , eu não consegui a saída desejada na bash 4.4.18 (1) (Ubuntu 16.04).
find . -type f -iname '*.c' -printf '%hshopt -s globstar dotglob nocaseglob
printf "%q\n" **/*.c | awk -F/ '{NF--; f++} !c[$0]++{d++} END {print f, d}'
' |
sort -z |
uniq -zc |
sed -zr 's/([0-9]) .*/ 1/' |
tr '$ shopt -s globstar dotglob nocaseglob
$ printf "%q\n" ./**/*.c | awk -F/ '{NF--; f++} !c[$0]++{d++} END {print f, d}'
34 15
$ echo $BASH_VERSION
4.4.18(1)-release
' '\n' |
awk '{f += $1; d += $2} END {print f, d}'
Mas o zsh funcionou bem e o comando pode ser simplificado:
$ printf "%q\n" ./**/*.c(D.:h) | awk '!c[$0]++ {d++} END {print NR, d}'
29 7
D
permite que este glob selecione arquivos de pontos, .
seleciona arquivos regulares (portanto, não links simbólicos) e :h
imprime apenas o caminho do diretório e não o nome do arquivo (como find
's %h
) (Veja seções em Geração de nome de arquivo e Modificadores ). Portanto, com o comando awk, precisamos apenas contar o número de diretórios exclusivos que aparecem e o número de linhas é a contagem de arquivos.