Shell-Script

怎麼可能除了隱藏的’。來自重命名它們的腳本的文件和目錄?

  • September 11, 2020

我有一個腳本可以重命名,去掉空格、每個目錄和文件。它遞歸地執行它:

#!/bin/bash

find -name "* *" -print0 | sort -rz | \
 while read -d $'\0' f; do mv -v "$f" "$(dirname "$f")/$(basename "${f// /_}")"; done

當我在我的~目錄中執行它時,腳本造成了一些意外的破壞,重命名了以下項目:

  • ./.cache/google-chrome/Default/Storage/ext/nmmhkkegccagdldgiimedpiccmgmieda/def/Code Cache
  • ./.cache/google-chrome/Default/Storage/ext/gfdkimpbcpahaombhbimeihdjnejgicl/def/Code Cache
  • ./.cache/google-chrome/Default/Code Cache

我希望我的腳本除了以.重命名它開頭的任何目錄或文件。你會怎麼寫這樣的劇本?是否需要為此目的使用正則表達式?

只需使用zsh’s zmv

#! /bin/zsh -
autoload -Uz zmv
zmv '(**/)(* *)' '$1${2// /_}'
  • zmv預設情況下跳過隱藏文件和隱藏目錄中的文件(除非您將(#qD)限定符傳遞給第一個參數),
  • 它首先處理文件深度sort -rz不保證您可以為此工作並且-prune不兼容-depth
  • 我們不需要呼叫dirname/ basename(如果有一些文件名以換行符結尾,這將很昂貴並且不能與命令替換一起正常工作)。
  • 它沒有不匹配任意字節序列的問題find*
  • 如果有一些衝突(比如同時存在 aa b ca_b cfiles 都將重命名為a_b_c),它將在開始時檢測到它並在進行任何重命名之前中止。

要使用 GNU 工具做一些事情,那就是:

#! /bin/bash -
export LC_ALL=C
while IFS= read <&3 -rd '' file; do
 dir=${file%/*} name=${file##*/}
 mv -i -- "$file" "$dir/${name// /_}"
done 3< <(
 find . -name '.?*' -prune -o -name '* *' -print0 |
   tac -s '')

這裡,

  • 將語言環境設置為與任何字節序列匹配,C*不僅僅是在使用者語言環境中形成有效字元的字節。請注意,消息(錯誤、提示…)隨後將以英語而不是使用者的語言發出。
  • 傳遞-itomv以防止無意中破壞文件。
  • 通過 fd 3 而不是 stdin 傳遞文件列表,因此mv -i提示有效。
  • 使用tac -s ''而不是sort -rz反轉輸出以確保葉子在它們所在的分支之前被重命名。
  • 用標準和參數擴展運算符替換dirname/ 。basename``${var##pattern}``${var%pattern}
  • 還傳遞-rtoread以禁用其對反斜杠的特殊處理。
  • 確保 forread $IFS為空,因此它不會從輸入記錄中刪除尾隨空格。

無需過多查看管道中的其他命令,您find可以避免輸入任何具有隱藏名稱的目錄:

find . ! -path . -name '.*' -prune -o -name '* *' -print0 | ...

這將從搜尋樹中刪除.名稱開頭的所有目錄,但.目錄(目前目錄)除外。它也將不再找到隱藏的名稱。


在查看了您的其餘程式碼後,我建議您改用rename(Perl 變體):

find . ! -path . -name '.*' -prune \
   -o -type f -name '* *' -execdir rename -n -v 'tr/ /_/' {} \;

這將搜尋包含空格的文件名,然後用下劃線替換其中的所有空格,同時忽略隱藏名稱並且不輸入隱藏目錄。

rename呼叫該實用程序-n以阻止它實際執行任何操作(這是“試執行”)。-n當您確信它在做正確的事情時刪除。

沒有rename,你會做類似的事情

find . ! -path . -name '.*' -prune \
   -o -type f -name '* *' -exec bash -c '
   for pathname do
       filename=${pathname##*/}
       echo mv "$pathname" "${pathname%/*}/${filename// /_}"
   done' bash {} +

也就是說,呼叫一個bash遍歷找到的名稱的內聯腳本,通過將空格更改為下劃線來重命名每個名稱。

echo當您確信這樣做是正確的時,請刪除。

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