操作 find -exec 中的 {} 返回字符串

操作 find -exec 中的 {} 返回字符串

我希望尽可能高效地完成此操作,以防有大量文件。我想要的是重命名我找到的所有文件并删除它们的后缀。

例如:

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

这是行不通的。如果它是一个普通的 bash 变量,${var%.*}则会返回var到最后一个..

答案1

启动 shell 以使用 shell 参数扩展运算符:

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

请注意,您不想在/tmp其他人可写的目录上执行此操作,因为这将允许恶意用户使您重命名.log文件系统上的任意文件(或将文件移动到任何目录)。

通过一些findandmv实现,您可以使用find -execdirandmv -T使其更安全:

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

或者使用rename(perl 变体),它只会执行rename()系统调用,因此不会尝试将文件移动到其他文件系统或目录中...

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

或者完成整个事情perl

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

但请注意,perls Find::File(与 GNU 相反find)不会进行安全的目录遍历,所以这也不是您想要做的事情/tmp


笔记。

¹ 攻击者可以创建一个/tmp/. /auth.log文件,并在find查找它和mv移动它之间(并且该窗口可以轻松地任意大)"/tmp/. "用符号链接替换目录,从而/var/log导致/var/log/auth.log重命名为/var/log/auth

² 更糟糕的是,攻击者可以创建/tmp/foo.log恶意crontab代码和/tmp/foo符号链接/etc/cron.d,让您移动该 crontab进入 /etc/cron.d。这就是歧义mv(也适用于cp并且ln至少)可以是搬去搬进。 GNUmv用它的-t(进入) 和-T) 选项。

³File::Find通过执行 来遍历目录chdir("/tmp"); read content; chdir("foo") ...; chdir("bar"); chdir("../..")...。因此,有人可以创建一个/tmp/foo/bar目录,并在适当的时候将其重命名为,/tmp/bar这样chdir("../..")您就可以进入/.

答案2

这是一行:

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

它启动一个 shell,将 {} 分配给 shell 内的适当变量,然后使用 shell 语法应用字符串操作。

相关内容