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 alfind
comando 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-exec
cláusula.Respuestas:
Puede usar
bash -c
dentro delfind -exec
comando y usar el parámetro posicional con el comando bash:De esa manera
{}
está provisto$1
.El
sh
antes 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 defind
comenzar.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_shell
se utiliza como$0
pará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-execdir
opción.Usted puede (o no) desea utilizar la
-iname
opción para que los archivos nombrados*.JPG
o*.JpG
o 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
cjpeg
puede escribir directamente en un archivo, la redirección podría evitarse como:fuente
cjpeg
tiene una opción que le permite escribir en un archivo con nombre, en lugar de la salida estándar. Si su versión defind
admite la-execdir
opció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, GNUfind
agrega./
al nombre. No hay un estándar para decir qué comportamiento es "correcto").fuente
find
compatible-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.jpg
lugar de./t2/aa.jpg
.cjpeg: can't open optimized_./aa.jpg
.find
y 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