if command in find -exec

First, your snippet executes the command

echo {} : ;if [ -f {} ]; then echo file; else echo directory;fi

because it needs its output to evaluate the command substitution. Since there is no file named {}, this produces the output

{} :
directory

Then the find command is executed with the arguments -exec, echo, {}, :, directory, so for every file, it outputs the file name followed by a space and : directory.

What you actually want to do is to execute the shell snippet echo {} :; … on each file found by find. This snippet must be executed by a shell spawned by find, not by the shell that starts find, since it is receiving data from find on its command line. Therefore you need to instruct find to run a shell:

find -exec sh -c 'echo {} : ;if [ -f {} ]; then echo file; else echo directory;fi' \;

This is better, but still not right. It'll work with some (not all) find implementations if your file names don't contain any special characters, but since you are interpolating the file name in a shell script, you allow file names to execute arbitrary shell commands, e.g. if you have a file called $(rm -rf /) then the command rm -rf / will be executed. To pass file names to the script, pass them as separate arguments.

Also the first echo prints a newline after the colon. Use echo -n (if your shell supports it) or printf to avoid this.

find -exec sh -c 'printf "%s :" "$0"; if [ -f "$0" ]; then echo file; else echo directory; fi' {} \;

You can use -exec … {} + to group shell invocations, which is faster.

find -exec sh -c 'for x; do printf "%s :" "$x"; if [ -f "$x" ]; then echo file; else echo directory; fi; done' _ {} +

Another way for executing if; then; else; fi together with find is:

find |
while read p; do if [ -f "$p" ]; then echo file; else echo directory; fi; done