我希望尽可能高效地完成此操作,以防有大量文件。我想要的是重命名我找到的所有文件并删除它们的后缀。
例如:
[/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
文件系统上的任意文件(或将文件移动到任何目录)。
通过一些find
andmv
实现,您可以使用find -execdir
andmv -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
但请注意,perl
s 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 语法应用字符串操作。