Exec nos permite pasar todos los argumentos a la vez con {} +o pasarlos uno por uno con{} \;
Ahora digamos que quiero cambiar el nombre de todos los archivos JPEG , no hay problema para hacer esto:
find . \( -name '*.jpg' -o -name '*.jpeg' \) -exec mv '{}' '{}'.new \;
Pero si necesito redirigir la salida, '{}'no es accesible después de la redirección.
find . \( -name '*.jpg' -o -name '*.jpeg' \) -exec cjpeg -quality 80 '{}' > optimized_'{}' \;
Esto no funcionaria. Tendría que usar un bucle for, almacenar el resultado de find en una variable antes de usarlo. Admitámoslo, es engorroso.
for f in `find . \( -name '*.jpg' -o -name '*.jpeg' \)`; do cjpeg -quality 80 $f > optimized_$f; done;
Entonces, ¿hay una mejor manera?

>falta en el tercer ejemplo de código?{}aparece en cadenas más largas, ya que estas cadenas no suelen expandirse.find, una vez, y aplicada alfindcomando en sí. El{}no tiene un significado especial en ese contexto. La redirección no es un argumento parafind, y ciertamente no es parte de la-execcláusula.Respuestas:
Puede usar
bash -cdentro delfind -execcomando y usar el parámetro posicional con el comando bash:De esa manera
{}está provisto$1.El
shantes de la{}cuenta la carcasa interior de su "nombre", la cadena que se utiliza aquí se utiliza en los mensajes de error, por ejemplo. Esto se discute más en esta respuesta en stackoverflow .fuente
Tiene una respuesta ( https://unix.stackexchange.com/a/481687/4778 ), pero esta es la razón.
La redirección
>, y también las tuberías|, y la$expansión, son realizadas por el shell antes de que se ejecute el comando. Por lo tanto, stdout se redirige aoptimized_{}, antes defindcomenzar.fuente
Es necesario citar la redirección para evitar que el shell actual la interprete.
Pero citarlo también evitará que la salida del comando sea redirigida.
La solución conocida a esto es llamar a un shell:
En este caso, la redirección (
>) se cita en el shell actual y funciona correctamente dentro del shell llamado. Elcalled_shellse utiliza como$0parámetro (el nombre) del shell hijo (sh).Eso funciona bien si se agrega un sufijo al nombre del archivo, pero no si usa un prefijo. Para que funcione un prefijo, debe eliminar ambos
./que encuentran el prefijo de los nombres de archivo con${1#./}y utilizar la-execdiropción.Usted puede (o no) desea utilizar la
-inameopción para que los archivos nombrados*.JPGo*.JpGo también se incluyen otras variaciones.Y, es posible que (o no) también desee llamar al shell una vez por directorio en lugar de una vez por archivo agregando un bucle (
for f do … ; done) y un+al final:Y, finalmente, como
cjpegpuede escribir directamente en un archivo, la redirección podría evitarse como:fuente
cjpegtiene una opción que le permite escribir en un archivo con nombre, en lugar de la salida estándar. Si su versión defindadmite la-execdiropción, puede aprovecharla para hacer innecesaria la redirección.Nota: en realidad, esto supone la versión BSD de
find, que parece eliminar el./nombre del archivo cuando se aplica{}. (O, por el contrario, GNUfindagrega./al nombre. No hay un estándar para decir qué comportamiento es "correcto").fuente
findcompatible-execdir, puede usar eso en lugar de-exec. Hace que el comando se ejecute en el directorio donde se encontró el archivo y{}se convertirá enaa.jpglugar de./t2/aa.jpg.cjpeg: can't open optimized_./aa.jpg.findy BSDfind. (Los peligros de usar extensiones no estándar.)./parece ser más propenso a errores. Se propone una solución razonable en esta respuesta .Cree un script cjq80:
Hazlo ejecutable
Y úsalo en -exec:
fuente