for f in ./*; do ls ./* | wc -l $f; done
Isso define f para todos os nomes de arquivos no diretório atual ( ./* ) por sua vez. Para cada um deles, ele executa ls novamente com todos os nomes de arquivos no diretório atual, canalizando a saída para wc -l $f . Agora, wc -l conta as linhas nos arquivos dados como argumentos, e olha para stdin somente se não houver nenhum. Então o pipe é ignorado por wc aqui e ele tenta ler qualquer arquivo que tenha o nome em f , falhando quando é um diretório.
Eu suspeito que o que você quer é algo assim
for f in ./* ; do ls "$f" | wc -l ; done
O qual executaria ls on no diretório nomeado em f e, em seguida, contaria as linhas nele. Mas ls pode ignorar arquivos cujos nomes começam com um ponto ( ls -A também os lista, ignorando . e .. ), e a glob pode também corresponder a arquivos regulares (se você tiver algum nesse diretório principal).
Mas você realmente não precisa de ls ou wc . OS X deve ter Bash, onde isso deve funcionar:
shopt -s dotglob # '*' matches files with a leading dot, too
for f in ./*/ ; do # trailing slash only picks directories
set -- "$f"/* # fill the positional params with the filenames in the dir
echo "$f $#" # print the directory name and number of params/files
done