comando if en find -exec

9

Solo estaba tratando de enumerar todos los directorios y archivos en el directorio actual y también escribir si son archivos o directorios con el siguiente comando:

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

Sé que es un comando tonto, puedo usar otras cosas como -type fo -type d, pero quiero saber por qué ese código no funcionó como esperaba. Simplemente imprime el directorio a todos ellos. Por ejemplo, mientras que la salida de findes:

.
./dir
./dir/file

La salida de mi código es:

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

Y salida de

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

es

dir/file : file

Estoy trabajando Ubuntu 14.10y usandofind (GNU findutils) 4.4.2

Esref
fuente
1
find -exec bash -c 'echo -n "{} : ";if [ -f "{}" ]; then echo file; else echo directory;fi' \;
Costas
1
Gracias @Costas pero sé que usando bash o sh puedo lograr mi objetivo inicial, pero ahora quiero entender por qué si se comporta de manera diferente con el parámetro exec.
Esref
Poner argumentos entre comillas "{}". ¿Por qué usas echodos veces?
Costas
Ya lo intenté y da el mismo resultado. encontrar -exec echo echo "{}" : ;if [ -f "{}" ]; then echo file; else echo directory;fi\;
Esref

Respuestas:

13

Primero, tu fragmento ejecuta el comando

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

porque necesita su salida para evaluar la sustitución del comando. Como no hay nombre de archivo {}, esto produce la salida

{} :
directory

A continuación, el findcomando se ejecuta con los argumentos -exec, echo, {}, :, directory, por lo que para cada archivo, imprime el nombre del archivo seguido de un espacio y : directory.

Lo que realmente quiere hacer es ejecutar el fragmento de shell echo {} :; …en cada archivo encontrado por find. Este fragmento debe ser ejecutado por un shell generado find, no por el shell que se inicia find, ya que está recibiendo datos de findsu línea de comando. Por lo tanto, debe instruir findpara ejecutar un shell:

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

Esto es mejor, pero aún no está bien. Funcionará con algunas (no todas) findimplementaciones si los nombres de sus archivos no contienen caracteres especiales, pero dado que está interpolando el nombre del archivo en un script de shell, permite que los nombres de archivo ejecuten comandos de shell arbitrarios, por ejemplo, si tiene un archivo llamado $(rm -rf /)entonces rm -rf /se ejecutará el comando . Para pasar nombres de archivo al script, páselos como argumentos separados.

También el primero echoimprime una nueva línea después de los dos puntos. Use echo -n(si su shell lo admite) o printfpara evitar esto.

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

Puede usar -exec … {} +para agrupar invocaciones de shell, que es más 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- deja de ser malvado'
fuente
3

Otra forma de ejecutar if; then; else; fijunto con findes:

find |
while read p; do if [ -f "$p" ]; then echo file; else echo directory; fi; done
ncomputadoras
fuente
1
Solución genial Evita patrones de cotizaciones complejas y complicaciones de subshell. Puede haber definido previamente una función de shell y ejecutarla en los archivos que coinciden con la condición "if".
gerlos