1

I want to make a script that browses my entire tree and delete all hidden files (ex ".git") except some (ex ".@__thumb"). I made a script that browses my tree and deletes all my files starting with a ".".

Here it is:

find . -depth -type d -name '.*' |
while read f
   do
                dn=`dirname "$f"`
                bn=`basename "$f"`
                mv "$dn/$bn" "$dn/${bn//.}"
done

I then wanted to adapt it and add a condition so that when my folder/file is called ".@__thumb" for example (which is a folder I don't want to delete) it doesn't delete the ".

I got this:

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

However, my condition seems to be useless. My script still deletes my ".@__thumb" folder. Do you have any idea where the problem could be coming from or any idea for improvement? I don't know if I was clear in my explanation. If you have any questions, don't hesitate. Thank you very much.

2
  • Your script only processes hidden directories. Can you clarify what exactly you want to delete? Also, reading the output of find won't work if one of the found files has a name that includes white space. It's bad practice; use find option -exec instead. Aug 4 at 8:43
  • As to the script, bn is not set initially, and I don't see it deleting anything. If you want to find out what happens, add an echo $bn at the beginning of the loop. Aug 4 at 8:48
5

I'm assuming here you want to remove the leading dot in the name of those hidden directories (as your code clearly attempts to do), not remove those directories themselves as your question seems to be asking.

Here, I'd use zsh:

autoload -Uz zmv
zmv -n '(**/).(^@__thumb)(#qD/)' '$1$2'

Remove the -n (dry-run) if happy.

With find, you'd use:

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 {} +

(assuming GNU mv for -T)

Your approach is not very robust and has a few issues:

  • to read a line with read, the syntax is IFS= read -r line, not read line. However, anyway, file paths can be made of several lines, you can't loop over find's output that way.
  • -name '.*' matches on files whose name starts with . and is followed by zero or more characters. So it will match on . itself, and it will skip the file names that contain sequences of bytes not forming valid characters in the locale. Hence LC_ALL=C to make sure all bytes are characters and .?* to make sure there's at least one character (byte with LC_ALL=C after the .).
  • [[ $a = $b ]] in bash is to check whether $a matches the $b pattern (same in reverse for !=). You need [[ $a = "$b" ]] or the standard [ "$a" = "$b" ] for byte-to-byte equality comparison (shouldn't make a difference here though as .@__thumb doesn't contain any wildcard character).
  • `...` is the deprecated ancient form of command substitution, I'd recommend you use the modern $(...) form instead. Both have the issue that they strip trailing newline characters though, so can't be used for arbitrary file names. Using the standard sh ${var##*/} and ${var%/*} operators instead of dirname/basename + command substitution avoids those problems.
  • the ${bn//.} ksh operator removes all the occurrences of . in $bn. So it would rename .foo.d to food. Use ${bn/.} to remove the first occurrence only, or the standard ${bn#.} to remove a leading . character.
  • if a directory contains both a .foo and foo dir, mv .foo foo becomes mv .foo foo/.foo (it moves .foo into (not to) the foo dir). -T avoids that and -i gives you the opportunity to avoid overwriting foo with .foo. zmv performs all the sanity checks before doing any operation so is even safer.
  • as already noted by @RalfFriedl, you reference $bn before setting it!
  • in any case, if you rename the .git directory to git, git will stop seeing its parent as a git repository. If you rename .bashrc to bashrc, bash will no longer read it upon start, etc.
4

While this is more related to programing in general, you have to be careful with the order of your instructions.

Here you use the value of $bn in the comparison of your condition, but you calculate the value later.

So try

...
bn=`basename "$f"`
if [[ $bn != $thumb ]]
...

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.