comando if em find -exec

9

Eu só estava tentando listar todos os diretórios e arquivos no diretório atual e também escrever se eles forem arquivo ou diretório com o seguinte comando:

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

Eu sei que é um comando bobo, posso usar outras coisas como -type for -type d, mas quero saber por que esse pedaço de código não funcionou como eu esperava. Ele apenas imprime o diretório para todos eles. Por exemplo, enquanto a saída de findé:

.
./dir
./dir/file

a saída do meu código é:

. : directory
./dir : directory
./dir/file : directory

E saída de

echo `echo dir/file : ;if [ -f dir/file ]; then echo file; else echo directory;fi`

é

dir/file : file

Estou trabalhando Ubuntu 14.10e usandofind (GNU findutils) 4.4.2

Esref
fonte
1
find -exec bash -c 'echo -n "{} : ";if [ -f "{}" ]; then echo file; else echo directory;fi' \;
Costas
1
Obrigado @Costas, mas eu sei que usando bash ou sh posso atingir meu objetivo inicial, mas agora quero entender por que se comportar de maneira diferente com o parâmetro exec.
Esref 26/02
Coloque argumentos entre aspas "{}". Por que você usa echoduas vezes?
Costas
Eu já tentei isso e dá a mesma saída. encontre -exec echo echo "{}" : ;if [ -f "{}" ]; then echo file; else echo directory;fi\;
Esref 26/02

Respostas:

13

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á arquivo nomeado {}, isso produz a saída

{} :
directory

Em seguida, o findcomando é executado com os argumentos -exec, echo, {}, :, directory, por isso para cada arquivo, ele exibe o nome do arquivo seguido por um espaço e : directory.

O que você realmente deseja fazer é executar o snippet de shell echo {} :; …em cada arquivo encontrado por find. Esse snippet deve ser executado por um shell gerado find, não pelo shell iniciado find, pois está recebendo dados de findsua linha de comando. Portanto, você precisa instruir findpara 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. Funcionará com algumas findimplementações (não todas) se os nomes de arquivos não contiverem caracteres especiais, mas como você interpola o nome do arquivo em um script de shell, permite que os nomes de arquivos executem comandos arbitrários de shell, por exemplo, se você tiver um arquivo chamado $(rm -rf /)então o comando rm -rf /será executado. Para passar nomes de arquivos para o script, passe-os como argumentos separados.

Além disso, o primeiro echoimprime uma nova linha após os dois pontos. Use echo -n(se o seu shell suportar) ou printfpara 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' _ {} +
Gilles 'SO- parar de ser mau'
fonte
3

Outra maneira de executar if; then; else; fijunto findé:

find |
while read p; do if [ -f "$p" ]; then echo file; else echo directory; fi; done
ncomputadores
fonte
1
Solução legal. Evita padrões complexos de cotações e complicações de subcamadas. Você pode ter definido anteriormente uma função shell e executá-la nos arquivos que correspondem à condição "se".
gerlos