Primeiro, seu snippet executa o comando
echo {} : ;if [ -f {} ]; then echo file; else echo directory;fi
porque precisa de sua saída para avaliar a substituição do comando. Como não há nenhum arquivo chamado {} , isso produz a saída
{} :
directory
Em seguida, o comando find é executado com os argumentos -exec , echo , {} , : , directory , portanto, para cada arquivo, ele exibe o nome do arquivo seguido por um espaço e : directory .
O que você realmente quer fazer é executar o fragmento de shell echo {} :; … em cada arquivo encontrado por find . Esse snippet deve ser executado por um shell gerado por find , não pelo shell que inicia find , pois está recebendo dados de find em sua linha de comando. Portanto, você precisa instruir find para executar um shell:
find -exec sh -c 'echo {} : ;if [ -f {} ]; then echo file; else echo directory;fi' \;
Isso é melhor, mas ainda não está certo. Ele funcionará com algumas (não todas) find implementações se os nomes dos arquivos não contiverem caracteres especiais, mas desde que você esteja interpolando o nome do arquivo em um shell script, você permite que nomes de arquivos executem comandos shell arbitrários, por exemplo. Se você tiver um arquivo chamado $(rm -rf /) , o comando rm -rf / será executado. Para passar nomes de arquivos para o script, passe-os como argumentos separados.
Além disso, o primeiro echo imprime uma nova linha depois dos dois pontos. Use echo -n (se o seu shell suportar) ou printf para evitar isso.
find -exec sh -c 'printf "%s :" "$0"; if [ -f "$0" ]; then echo file; else echo directory; fi' {} \;
Você pode usar -exec … {} + para agrupar invocações de shell, o que é mais rápido.
find -exec sh -c 'for x; do printf "%s :" "$x"; if [ -f "$x" ]; then echo file; else echo directory; fi; done' _ {} +