Chris answer (roughly "ls -1 | wc -l") is correct for the first question. It counts everything (files, directories, symlinks, block devices, char devices, named pipes, named sockets, special files, etc)
Now, if you want to filter, that is a little bid harder.
One solution, in the spirit of that answer, would be to use -F option of ls, and then filter with grep, before counting
Number of directories:
ls -1 -F | grep "/$" | wc -l
Number of symbolic links:
ls -1 -F | grep "@$" | wc -l
Number of pipes:
ls -1 -F | grep "|$" | wc -l
Number of sockets:
ls -1 -F | grep "=$" | wc -l
There is no indicator in -F output for regular files. You can detect the absence of indicator
ls -1 -F | grep "[^=/@|]$" | wc -l
But theoretically, that is not correct. Since nothing prevent a regular file to actually end with one of those indicator (except the /, which is forbidden). So you if you do it like this, you are relying on the fact that nobody would ever create, in one of the directories you are interested in, a file whose name ends with an indicator of ls -F
If you want to also count hidden files (starting with a point), you have to add option "-A" to all those "ls".
For example, for regular files:
ls -A -1 -F | grep "[^=/@|]$" | wc -l
Another, more reliable, way to do it, is to use find.
Find is recursive (it list all files in a given directory, and in its subdirectories, and subdirectories of subdirectories, etc)
But with option "-maxdept 1", find will not explore anything under the first level.
So it is almost the same. Except that find count the directory (/bin in your example), so you have to remove 1 to the result for files)
find /bin -maxdepth 1 | wc -l
counts the number of files in /bin, "/bin" included. So remove 1 for what you want.
Then, you can ask find to filter using many of find options (see "man find" for details) depending on a bunch of criteria, among which file type.
For example
find /bin/ -maxdepth 1 -type f | wc -l
counts all regular files under /bin (this time, no need to substract 1, since /bin is not a regular file)
Directories:
find /bin/ -maxdepth 1 -type d | wc -l
(there you must substract 1, since /bin/ is a directory)
Symbolic links:
find /bin/ -maxdepth 1 -type l | wc -l
Last boring remark: theoretically, you could have a carriage return in a filename. Current implementation of find on my linux distribution replaces them with "?", so that would not change the count. But some other might really print the "\n" (last time I tried, it was doing so).
So, theoretically, those commands might count twice a file whose name contains a "carriage return".
So, you can instead, since, after all, you don't really need find to display the actual file names, use this command (you just want find to display one thing per file, and then count the number of printed things. Whether that thing is the file name, or something else doesn't matter):
find /bin/ -type f -printf "x" | wc -c
(find displays a "x" for every regular file in /bin/. And wc -c counts the number of x. Sure, that usage of printf is quite strange. Printf is mainly made to print interesting data, like find -printf "%f %a\n" which prints all files with their last access time. But after all, why not just printing "x"?)
Which is my final and best answer, I guess.
find DIRECTORY -maxdepth 1-type TYPE -printf x | wc -c
In a script, named count, that would be:
find "$2" -maxdepth 1 -type $1 -printf x | wc -c
If you insist on the option (first arg) to be "-f", "-d", ... instead of just "f" "d"...
find "$2" -maxdepth 1 -type ${1##-} -printf x | wc -c
${x##-} is x without "-" prefix
cnt=$(find "$2" -maxdepth 1 -type ${1##-} -printf x | wc -c)
[[ "$1" = "-d" ]] && ((cnt--))
echo $cnt
In one line, just for fun, and obfuscation:
echo $(( $(find "$2" -maxdepth 1 -type ${1##-} -printf x | wc -c) - $(find "$2" -maxdepth 0 -type ${1##-} -printf x | wc -c) ))
(With, also for fun, another way to deal with the "-d" case: instead of substracting 1 for the directory, we substract the result of the same find with maxdepth 0. This result will be 1 in the case of -d. And 0 else. Advantage of doing so: it will works even in strange cases, like if "/bin" is a symlink itself)
So, final answer:
echo $(( $(find "$2" -maxdepth 1 -type ${1##-} -printf x | wc -c) - $(find "$2" -maxdepth 0 -type ${1##-} -printf x | wc -c) ))
Just one line. And it works with any strange cases you could thing of (files with indicators, or carriage returns in it, etc)