Nir*_*Nir 4 scripting bash find shell-script
我希望尽可能高效,以防有很多文件。我想要的是重命名我找到的所有文件并删除它们的后缀。
例如:
[/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
Run Code Online (Sandbox Code Playgroud)
这不起作用。如果它是一个普通的 bash 变量,它${var%.*}会一直返回var到最后一个..
启动 shell 以使用 shell 参数扩展运算符:
find ~/tmp -name '*.log' -type f -exec sh -c '
for file do
mv -i -- "$file" "${file%.*}"
done' sh {} +
Run Code Online (Sandbox Code Playgroud)
请注意,您不希望在/tmp其他人可写的目录或任何其他人可写的目录上执行此操作,因为这会允许恶意用户让您重命名.log文件系统¹ 上的任意文件(或将文件移动到任何目录²)。
通过一些find和mv实现,您可以使用find -execdir并mv -T使其更安全:
find /tmp -name '*.log' -type f -execdir sh -c '
for file do
mv -Ti -- "$file" "${file%.*}"
done' sh {} +
Run Code Online (Sandbox Code Playgroud)
或者使用rename(perl 变体)只会执行rename()系统调用,因此不要尝试将文件移动到其他文件系统或目录...
find /tmp -name '*.log' -type f -execdir rename 's/\.log$//' {} +
Run Code Online (Sandbox Code Playgroud)
或者做整个事情perl:
perl -MFile::Find -le '
find(
sub {
if (/\.log\z/) {
$old = $_;
s/\.log\z//;
rename($old, $_) or warn "rename $old->$_: $!\n"
}
}, @ARGV)' ~/tmp
Run Code Online (Sandbox Code Playgroud)
但请注意,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( into ) 和-T( to ) 选项修复它。
³File::Find通过执行遍历目录chdir("/tmp"); read content; chdir("foo") ...; chdir("bar"); chdir("../..")...。因此,有人可以创建一个/tmp/foo/bar目录,并在适当的时候,它重命名为/tmp/bar这样chdir("../..")将土地你/。
小智 6
这是一行:
find /tmp -name "*.log" -type f -exec sh -c 'f="{}"; mv "$f" "${f%.*}"' \;
Run Code Online (Sandbox Code Playgroud)
它启动一个 shell,将 {} 分配给 shell 内的适当变量,然后使用 shell 语法应用字符串操作。