Manipular {} cadena de retorno desde find -exec

Manipular {} cadena de retorno desde find -exec

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 varhasta 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 /tmpningún directorio en el que otros puedan escribir, ya que eso permitiría a usuarios malintencionados obligarlo a cambiar el nombre .logde archivos arbitrarios en el sistema de archivos¹ (o mover archivos a cualquier directorio²).

Con algunas findimplementaciones mv, puedes usar find -execdiry mv -Tpara 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::Filea 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.logarchivo y, entre findencontrarlo y mvmoverlo (y esa ventana puede fácilmente hacerse arbitrariamente grande), reemplazar el "/tmp/. "directorio con un enlace simbólico para /var/logque se /var/log/auth.logle cambie el nombre a/var/log/auth

² Mucho peor, un atacante puede crear un enlace /tmp/foo.logmalicioso crontab, por ejemplo, y /tmp/fooun enlace simbólico /etc/cron.dy obligarte a mover ese crontab.en /etc/cron.d. Esa es la ambigüedad con mv(también se aplica a cpy lnal menos) que puede ser tantomover ayentra. GNU mvlo arregla con su -t(en) y -T(a) opciones.

³ File::Findrecorre el directorio haciendo chdir("/tmp"); read content; chdir("foo") ...; chdir("bar"); chdir("../..").... Entonces alguien puede crear un /tmp/foo/bardirectorio y, en el momento adecuado, cambiarle el nombre para /tmp/barque 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.

información relacionada