¿Cómo puedo usar dos comandos bash en -exec del comando find?

32

¿Es posible usar 2 comandos en la -execparte del findcomando?

He intentado algo como:

find . -name "*" -exec  chgrp -v new_group {}  ; chmod -v 770 {}  \;

y obtengo:

find: falta argumento para -exec
chmod: no se puede acceder {}: No existe tal archivo o directorio
chmod: no se puede acceder ;: No existe dicho archivo o directorio

Luc M
fuente

Respuestas:

44

En cuanto al findcomando, también puede agregar más -execcomandos seguidos:

find . -name "*" -exec chgrp -v new_group '{}' \; -exec chmod -v 770 '{}' \;

Tenga en cuenta que este comando es, en su resultado, equivalente a usar

archivo chgrp -v nuevo_grupo && archivo chmod -v 770

en cada archivo

Todos los findparámetros 's tales como -name, -exec, -sizeetc., son en realidad pone a prueba : findse continuará ejecutando uno por uno, siempre y cuando toda la cadena hasta el momento ha evaluado a verdadera . Por lo tanto, cada -execcomando consecutivo se ejecuta solo si los anteriores devuelven verdadero (es decir, el 0estado de salida de los comandos). Pero findtambién comprende operadores lógicos como or ( -o) y not ( !). Por lo tanto, para usar una cadena de -execpruebas independientemente de los resultados anteriores, uno debería usar algo como esto:

find . -name "*" \( -exec chgrp -v new_group {} \; -o -exec chmod -v 770 {} \; \)
rozcietrzewiacz
fuente
44
+1: Sí, esa es la forma más elegante de hacerlo. Si puede explicar por qué usa '{}'(apóstrofos alrededor de las llaves), visite: unix.stackexchange.com/q/8647/4485
usuario desconocido el
1
@user Desafortunadamente, no sé si aún es necesario. Hice algunas pruebas en este momento y no me he encontrado con una situación en la que cambiaría nada. Supongo que es solo una "buena práctica" lo que desaparecerá.
rozcietrzewiacz
2
Las comillas son importantes para archivos con espacios en sus nombres.
naught101
17
find . -name "*" -exec sh -c 'chgrp -v new_group "$0" ; chmod -v 770 "$0"' {} \;
Glenn Jackman
fuente
@Gilles: Las maravillas del -cmanejo extraño de $ 0 me hacen pensar que esto está mal cada vez que lo miro, pero definitivamente es correcto.
derobert
Me gusta que se defina el shell explícito ...
djangofan
Esta respuesta (y la respuesta de Giles) parece ser la mejor respuesta para la pregunta dada la sh -c.
14

El comando analiza primero su comando en dos comandos separados por a ;, que es equivalente a una nueva línea:

find . -name "*" -exec chgrp -v new_group {}
chmod -v 770 {} \;

Si desea ejecutar un comando de shell, invoque un shell explícitamente con bash -c(o sh -csi no le importa que el shell sea específicamente bash):

find . -name "*" -exec sh -c 'chgrp -v new_group "$0"; chmod -v 770 "$0"' {} \;

Tenga en cuenta el uso de {}como argumento para el shell; es el argumento cero (que normalmente es el nombre del shell o script, pero esto no importa aquí), por lo tanto, se hace referencia a él como "$0".

Puede pasar varios nombres de archivo al shell a la vez y hacer que el shell los repita, será más rápido. Aquí paso _como el nombre del script y los siguientes argumentos son nombres de archivo, que for x(un atajo para for x in "$@") itera.

find . -name "*" -exec sh -c 'for x; do chgrp -v new_group "$x"; chmod -v 770 "$x"; done' _ {} +

Tenga en cuenta que desde bash 4, o en zsh, no necesita encontrar nada aquí. En bash, ejecuta shopt -s globstar(ponlo en tu ~/.bashrc) para activar la **/posición de un globo de directorio recursivo. (En zsh, esto está activo todo el tiempo). Luego

chgrp -v new_group -- **/*; chmod -v 770 -- **/*

o si desea que los archivos se repitan en orden

for x in **/*; do
  chgrp -v new_group -- "$x"
  chmod -v 770 -- "$x"
done

Una diferencia con el findcomando es que el shell ignora los archivos de puntos (archivos cuyo nombre comienza con a .). Para incluirlos, en bash, primer set GLOBIGNORE=.:..; en zsh, úsalo **/*(D)como patrón glob.

Gilles 'SO- deja de ser malvado'
fuente
Esta respuesta (y la respuesta de Glenn) parece ser la mejor respuesta para la pregunta dada la sh -c.