Bash

從 find -exec 操作 {} 返回字元串

  • July 6, 2018

如果有很多文件,我想盡可能高效地執行此操作。我想要的是重命名我找到的所有文件並刪除它們的後綴。

例如:

[/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到最後一個..

啟動一個 shell 以使用 shell 參數擴展運算符:

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

請注意,您不想在/tmp其他人可寫的任何目錄上執行此操作,因為這將允許惡意使用者讓您重命名.log文件系統上的任意文件¹(或將文件移動到任何目錄²)。

通過一些findmv實現,您可以使用find -execdirmv -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移動到的歧義。GNU用它的( into ) 和( to ) 選項來修復它。ln``mv``-t``-T

³File::Find通過執行遍歷目錄chdir("/tmp"); read content; chdir("foo") ...; chdir("bar"); chdir("../..")...。因此,有人可以創建一個/tmp/foo/bar目錄,並在適當的時候將其重命名/tmp/bar為.chdir("../..")``/

引用自:https://unix.stackexchange.com/questions/406012