Quiero hacerlo lo más eficientemente posible en caso de que haya muchos archivos.
Lo que quiero es cambiar el nombre de todos los archivos que encontré y eliminar su sufijo.
Por ejemplo:
[/tmp] $ ls -l
a.log
b.log
c.tmp
[/tmp] $ find /tmp -name "*.log" -type f -exec mv {} {%.*} \;
[/tmp] $ ls -l
a
b
c.tmp
Esto no funciona. Si fuera una variable bash normal ${var%.*} habría devuelto var hasta el último . .
Respuesta aceptada:
Inicie un shell para usar operadores de expansión de parámetros de shell:
find ~/tmp -name '*.log' -type f -exec sh -c '
for file do
mv -i -- "$file" "${file%.*}"
done' sh {} +
Tenga en cuenta que no desea hacer eso en /tmp o cualquier directorio en el que otros puedan escribir, ya que eso permitiría a los usuarios maliciosos hacerle cambiar el nombre de .log arbitrario archivos en el sistema de archivos¹ (o mover archivos a cualquier directorio²).
Con algo de find y mv implementaciones, puede usar find -execdir y mv -T para hacerlo más seguro:
find /tmp -name '*.log' -type f -execdir sh -c '
for file do
mv -Ti -- "$file" "${file%.*}"
done' sh {} +
O use rename (la variante de perl) que simplemente haría un rename() llamada al sistema para no intentar mover archivos a otros sistemas de archivos o directorios...
find /tmp -name '*.log' -type f -execdir rename 's/\.log$//' {} +
O hazlo todo en perl :
perl -MFile::Find -le '
find(
sub {
if (/\.log\z/) {
$old = $_;
s/\.log\z//;
rename($old, $_) or warn "rename $old->$_: $!\n"
}
}, @ARGV)' ~/tmp
Pero tenga en cuenta que perl ‘s Find::File (al contrario de GNU find ) no realiza un recorrido de directorio seguro³, por lo que no es algo que le gustaría hacer en /tmp tampoco.
Notas.
¹ un atacante puede crear un /tmp/. /auth.log archivo, y entre find encontrarlo y mv moviéndolo (y esa ventana puede hacerse fácilmente arbitrariamente grande) reemplace "/tmp/. " directorio con un enlace simbólico a /var/log resultando en /var/log/auth.log siendo renombrado a /var/log/auth
² Mucho peor, un atacante puede crear un /tmp/foo.log crontab malicioso por ejemplo y /tmp/foo un enlace simbólico a /etc/cron.d y hacer que muevas ese crontab a /etc/cron.d . Esa es la ambigüedad con mv (también se aplica a cp y ln al menos) que puede ser tanto mover a y mover a . GNU mv lo arregla con su -t (hacia ) y -T (a ) opciones.
³ File::Find atraviesa el directorio haciendo chdir("/tmp"); read content; chdir("foo") ...; chdir("bar"); chdir("../..")... . Entonces alguien puede crear un /tmp/foo/bar directorio y en el momento adecuado, cámbiele el nombre a /tmp/bar entonces chdir("../..") te llevaría a / .