Manipular {} cadeia de retorno de find -exec

3

Eu quero fazer o mais eficiente possível, caso haja muitos arquivos. O que eu quero é renomear todos os arquivos que encontrei e remover o sufixo deles.

Por exemplo:

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

Isso não funciona. Se fosse uma variável bash normal ${var%.*} teria retornado var até o último . .

    
por Nir 21.11.2017 / 14:32

2 respostas

6

Inicie um shell para usar os operadores de expansão de parâmetros do shell:

find ~/tmp -name '*.log' -type f -exec sh -c '
  for file do
    mv -i -- "$file" "${file%.*}"
  done' sh {} +

Observe que você não deseja fazer isso em /tmp ou em qualquer diretório gravável por outros, pois isso permitiria que usuários maliciosos o fizessem renomear arquivos .log arbitrários no sistema de arquivos¹ (ou mover arquivos para qualquer diretório²) .

Com algumas implementações find e mv , você pode usar find -execdir e mv -T para torná-lo mais seguro:

find /tmp -name '*.log' -type f -execdir sh -c '
  for file do
    mv -Ti -- "$file" "${file%.*}"
  done' sh {} +

Ou use rename (a variante perl) que faria apenas uma chamada de sistema rename() para não tentar mover arquivos para outros sistemas de arquivos ou diretórios ...

find /tmp -name '*.log' -type f -execdir rename 's/\.log$//' {} +

Ou faça tudo em perl :

perl -MFile::Find -le '
  find(
    sub {
      if (/\.log\z/) {
        $old = $_;
        s/\.log\z//;
        rename($old, $_) or warn "rename $old->$_: $!\n"
      }
    }, @ARGV)' ~/tmp

Mas note que perl do Find::File (ao contrário do GNU find ) não faz um diretório seguro traversal³, então isso não é algo que você gostaria de fazer no /tmp .

Notas.

¹ um invasor pode criar um arquivo /tmp/. /auth.log e entre find localizá-lo e mv movê-lo (e essa janela pode ser facilmente arbitrariamente grande) substitua o diretório "/tmp/. " por um link simbólico para /var/log resultando em /var/log/auth.log sendo renomeado para /var/log/auth

² Muito pior, um invasor pode criar um /tmp/foo.log malicioso crontab por exemplo e /tmp/foo um link simbólico para /etc/cron.d e fazer você mover esse crontab para /etc/cron.d . Essa é a ambigüidade com mv (também se aplica a cp e ln pelo menos) que pode ser mover para e mover para . O% GNUmv corrige com suas opções -t ( em ) e -T ( para ).

³ File::Find atravessa o diretório fazendo chdir("/tmp"); read content; chdir("foo") ...; chdir("bar"); chdir("../..")... . Então, alguém pode criar um diretório /tmp/foo/bar e, no momento certo, renomeá-lo para /tmp/bar , para que chdir("../..") coloque você em / .

    
por 21.11.2017 / 14:39
2

Aqui está um verso:

find /tmp -name "*.log" -type f -exec sh -c 'f="{}"; mv "$f" "${f%.*}"' \;

Ele inicia um shell, atribui o {} a uma variável apropriada dentro do shell e, em seguida, aplica as manipulações de string usando a sintaxe do shell.

    
por 06.07.2018 / 14:46