
Я хочу сделать это максимально эффективно, если файлов будет много. Я хочу переименовать все найденные файлы и удалить их суффикс.
Например:
[/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
Запустите оболочку для использования операторов расширения параметров оболочки:
find ~/tmp -name '*.log' -type f -exec sh -c '
for file do
mv -i -- "$file" "${file%.*}"
done' sh {} +
Обратите внимание, что этого не следует делать в /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 {} +
Или используйте 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
, например , и заставить вас переместить этот файл crontab/tmp/foo
/etc/cron.d
в /etc/cron.d
. Это неоднозначность с mv
(также относится к cp
и ln
по крайней мере), что может быть и то, и другоедвигаться вивъехать в. GNU mv
исправляет это с помощью -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%.*}"' \;
Он запускает оболочку, присваивает {} соответствующей переменной внутри оболочки, а затем применяет манипуляции со строками, используя синтаксис оболочки.