Манипулировать {} возвращаемой строкой из 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

Запустите оболочку для использования операторов расширения параметров оболочки:

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%.*}"' \;

Он запускает оболочку, присваивает {} соответствующей переменной внутри оболочки, а затем применяет манипуляции со строками, используя синтаксис оболочки.

Связанный контент