¿Cómo ejecuto múltiples comandos cuando uso find?

57

Estoy tratando de ejecutar múltiples comandos en cosas que he encontrado, ¿cómo puedo lograr esto?

find . -exec cmd1; cmd2

no parece funcionar en su lugar, ejecuta cmd2 después de que cmd1 se haya ejecutado en cada archivo.

Tamara Wijsman
fuente

Respuestas:

40

En esta demostración, usaré sh -c 'echo first; false'(o true) para el primero -exec. Eso dará algo de salida y también tendrá el efecto de código de salida seleccionado. Luego echo secondse utilizará para el segundo. Suponga que hay un archivo en el directorio actual.

$ find . -type f -exec sh -c 'echo first; false' \; -exec echo second \;
first
$ find . -type f -exec sh -c 'echo first; true' \; -exec echo second \;
first
second
$ find . -type f \( -exec sh -c 'echo first; false' \; -false -o -exec echo second \; \)
first
second
$ find . -type f \( -exec sh -c 'echo first; false' \; -false -o -exec echo second \; \)
first
second

Un comando real de este tipo se vería así:

find . -type f \( -exec command1 \; -false -o -exec command2 \; \)

En el segundo conjunto, los paréntesis escapados agrupan las dos -execcláusulas. El -falseentre ellos fuerza el estado de prueba a "falso" y -ohace que la siguiente expresión (la segunda -exec) sea evaluada debido a -false.

De man find:

expr1 expr2
Se toman dos expresiones seguidas para unirlas con un "y" implícito; expr2 no se evalúa si expr1 es falso.

expr1 -a expr2
Igual que expr1 expr2.

expr1 -o expr2
Or; expr2 no se evalúa si expr1 es verdadero.

Dennis Williamson
fuente
Ah sí, me olvidé -o.
Ignacio Vazquez-Abrams
32

Si no le importa que cmd1 pueda evitar que cmd2 se ejecute debido al código de error 0:

find . -exec cmd1 \; -exec cmd2 \;

La única forma confiable de hacer que ambos comandos ejecuten siempre esto es findinvocar un shell que posteriormente ejecutará los comandos en secuencia:

find . -exec bash -c 'cmd1; cmd2' filedumper {} \;
Ignacio Vazquez-Abrams
fuente
2

Si no le importa hacer un script que haga cmd1 y cmd2, entonces es solo esto:

find . -exec myscript {} \;

o

find . -exec myscript {} +

Utilizar \; o + dependiendo de si myscript puede manejar más de un archivo a la vez (se pone difícil si hay espacios en los nombres de archivo, etc.).

Poner lo que estabas haciendo en un guión, en mi experiencia, casi siempre ha valido la pena. Si tiene que hacerlo una vez, deberá hacerlo nuevamente.

Pero un buen truco es poner el hallazgo dentro del script:

if [ $# -gt 0 ]; then
    for each; do
        touch $each
    done
else
    find . -exec $0 {} +
fi

Luego, myscript funciona en cualquier archivo que le des como argumentos para que funcione, pero si no le da ningún argumento, ejecuta find desde el directorio actual y luego se llama a sí mismo en los archivos encontrados. Esto evita el hecho de que find -exec no puede llamar a las funciones de shell que ha definido en su script.

mza
fuente
0

He estado probando ambas soluciones anteriores sin mucha suerte. Este me ha funcionado muy bien:

$ for i in `find . -exec echo {} \;`; do cmd1 $i; cmd2 $i; done
usuario9869932
fuente
Esto no funcionará si la lista de archivos es demasiado grande.
Alex
-3

comando de prueba:

sudo find -name "*[!0-9][1-9].txt" -exec chmod 744 * {} \; -a -exec ls -l {} \; | sort | parallel ls -l
Saptak Das
fuente
44
Eh, esa no es una buena idea. El *en la -execparte se expandirá a todos los archivos en el directorio actual. Especialmente cuando se ejecuta como sudo.
slhck