如何只删除一些隐藏文件?

Fro*_*d0n 1 linux bash shell-script

我想制作一个脚本来浏览我的整个树并删除所有隐藏文件(例如“.git”),除了一些(例如“.@__thumb”)。我制作了一个脚本来浏览我的树并删除所有以“.”开头的文件。

这里是:

find . -depth -type d -name '.*' |
while read f
   do
                dn=`dirname "$f"`
                bn=`basename "$f"`
                mv "$dn/$bn" "$dn/${bn//.}"
done
Run Code Online (Sandbox Code Playgroud)

然后我想调整它并添加一个条件,以便当我的文件夹/文件被称为“.@__thumb”时(这是一个我不想删除的文件夹)它不会删除“.@__thumb”。

我懂了:

thumb=".@__thumb"
echo $thumb
 
find . -depth -type d -name '.*' |
while read f
   do
        if [[ $bn != $thumb ]]
        then
                dn=`dirname "$f"`
                bn=`basename "$f"`
                mv "$dn/$bn" "$dn/${bn//.}"
        fi
done
Run Code Online (Sandbox Code Playgroud)

然而,我的条件似乎没有用。我的脚本仍然会删除我的“.@__thumb”文件夹。你知道问题可能来自哪里或任何改进的想法吗?我不知道我的解释是否清楚。如果您有任何疑问,请不要犹豫。非常感谢。

Sté*_*las 5

我在这里假设您想删除那些隐藏目录名称中的前导点(正如您的代码显然试图这样做),而不是像您的问题所问的那样删除这些目录本身。

在这里,我会使用zsh

autoload -Uz zmv
zmv -n '(**/).(^@__thumb)(#qD/)' '$1$2'
Run Code Online (Sandbox Code Playgroud)

-n如果满意,请删除(试运行)。

使用find,您将使用:

LC_ALL=C find . -depth -name '.?*' ! -name '.@_thumb' -type d -exec sh -c '
  for file do
    base=${file##*/}
    mv -i -T "$file" "${file%/*}/${base#.}"
  done' sh {} +
Run Code Online (Sandbox Code Playgroud)

(假设 GNUmv-T

您的方法不是很稳健,并且存在一些问题:

  • 阅读一行read,语法是IFS= read -r line,不是read line。但是,无论如何,文件路径可以由多行组成,您不能find以这种方式循环遍历的输出
  • -name '.*'匹配名称以 开头.并后跟零个或多个字符的文件。所以它会匹配.自己,并且会跳过包含在语言环境中不形成有效字符的字节序列的文件名。因此LC_ALL=C,要确保所有字节都是字符,.?*并确保至少有一个字符(在LC_ALL=C之后的字节.)。
  • [[ $a = $b ]]inbash是检查是否$a匹配$b模式(反之亦然!=)。您需要[[ $a = "$b" ]][ "$a" = "$b" ]字节到字节相等比较的标准(尽管.@__thumb不包含任何通配符,但在此处不应有所作为)。
  • `...`是不推荐使用的古老形式的命令替换,我建议您改用现代$(...)形式。两者都有一个问题,即它们去除尾随的换行符,因此不能用于任意文件名。使用标准sh ${var##*/}${var%/*}运算符而不是目录名/基名 + 命令替换可以避免这些问题。
  • ${bn//.}KSH操作消除了所有出现.$bn。所以它会重命名.foo.dfood. 用于${bn/.}仅删除第一次出现,或${bn#.}用于删除前导.字符的标准。
  • 如果目录中同时包含.foofoo目录,mv .foo foo成为mv .foo foo/.foo(它移动.foo (不)的foo目录)。-T避免了这种情况,并-i让您有机会避免foo使用.foo. zmv在执行任何操作之前执行所有健全性检查,因此更安全。
  • 正如@RalfFriedl 已经指出的那样,您$bn在设置之前参考!
  • 在任何情况下,如果您将.git目录重命名为gitgit将不再将其父目录视为 git 存储库。如果您重命名.bashrcbashrcbash则在开始时将不再读取它,等等。