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 /
.