I would like to remove the "." in the names of my folders and subfolders.

I have folders called "Archive.1"; "Archive.2" (etc.) in which I have folders called "Document.1" etc. (etc...) in which I have folders called "Document.1" etc. I want to remove all the "." and have "Archive1" and inside "Document1".

I have a semblance of the beginning of a script:

for f in *.mp4; do fn=`echo $f|sed 's/\.//g'`; mv "$f" "$fn"; done

But it only browses the folder I'm in.

Can you help me?

Thanks in advance.

  • You say your files are called e.g. Document.1 and that your directories are called e.g. Archive.1, but in your own attempt you seem to be searching for names ending in .mp4. Could you please clarify what names need to be changed? Should the dot in front of the mp4 filename suffix be removed? Should filenames containing less than two dots be filtered out? – Kusalananda Jul 21 at 7:25

You probably want to use find, which (by default) searches a whole subtree recursively.

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

The find command says to match every directory (type d) whose name contains a .. The -depth option makes it perform a post-order traversal on the current directory tree, i.e. check the contents of each directory before that directory itself, so matching directories containing other matching directories (e.g. "a.b/c.d") will be output as matches after matches of descendants (so, "a.b/c.d" followed by "a.b"). Without this, the loop could fail, because find produces the whole list, without knowing or caring that parts of paths might change partway; the loop, only acting upon the precompiled list, would do "a.b" -> "ab" and then try to do "a.b/c.d" -> "a.b/cd". A post-order traversal gets around such a problem.

Filenames with spaces in them are always a pain to deal with in Bash, since it tends to treat them as argument delimiters. The above works around that using the read builtin, which treats only newlines as delimiters.

Since we only want to operate on the name of each directory, and not its whole pathname, we split it into the part before the last slash ("dirname") and the actual filename ("basename") and operate on only the basename.

Also, pattern substitution can handle the transformation easily without resorting to an external filter. The general form is ${parameter/pattern/string}, which should look familiar compared to sed. However, here, starting pattern with another slash is used to indicate all matches should be substituted, like /g in sed. Also, since string is empty, the slash before it can be omitted.

Not entirely sure why you were trying to match *.mp4. Are you looking only for paths with .mp4 files in them? If so, then things are a little more complex. We would need to first produce a list of pathnames for such files, and then filter out a list of directory pathnames from those, and make sure to operate on them in the proper order.

  • Thank you very much, it works perfectly. I wasn't on the right track at all. – Ekip_DetP Jul 21 at 17:33
  • If that's so, you should mark the answer as accepted. 😄 – ddawson Jul 21 at 18:44

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.