操作 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

啟動 shell 以使用 shell 參數擴展運算子:

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

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

透過一些findandmv實現,您可以使用find -execdirandmv -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

但請注意,perls Find::File(與 GNU 相反find)不會進行安全的目錄遍歷,所以這也不是您想要做的事情/tmp


筆記。

1 攻擊者可以建立一個/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 語法應用字串操作。

相關內容