
Quiero hacerlo lo más eficiente 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 regresado var
hasta el último .
.
Respuesta1
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
ningún directorio en el que otros puedan escribir, ya que eso permitiría a usuarios malintencionados obligarlo a cambiar el nombre .log
de archivos arbitrarios en el sistema de archivos¹ (o mover archivos a cualquier directorio²).
Con algunas find
implementaciones mv
, puedes 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 una 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
( Find::File
a diferencia de GNU find
) no realiza un recorrido seguro del directorio³, por lo que tampoco es algo que le gustaría hacer /tmp
.
Notas.
¹ un atacante puede crear un /tmp/. /auth.log
archivo y, entre find
encontrarlo y mv
moverlo (y esa ventana puede fácilmente hacerse arbitrariamente grande), reemplazar el "/tmp/. "
directorio con un enlace simbólico para /var/log
que se /var/log/auth.log
le cambie el nombre a/var/log/auth
² Mucho peor, un atacante puede crear un enlace /tmp/foo.log
malicioso crontab
, por ejemplo, y /tmp/foo
un enlace simbólico /etc/cron.d
y obligarte a mover ese crontab.en /etc/cron.d
. Esa es la ambigüedad con mv
(también se aplica a cp
y ln
al menos) que puede ser tantomover ayentra. GNU mv
lo arregla con su -t
(en) y -T
(a) opciones.
³ File::Find
recorre 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, cambiarle el nombre para /tmp/bar
que chdir("../..")
llegue a /
.
Respuesta2
Aquí hay una sola línea:
find /tmp -name "*.log" -type f -exec sh -c 'f="{}"; mv "$f" "${f%.*}"' \;
Lanza un shell, asigna {} a una variable adecuada dentro del shell y luego aplica las manipulaciones de cadena utilizando la sintaxis del shell.