Renaming files in a directory and its subdirectories

90 Views Asked by At

I'm trying to replace special characters with underscores (_) in the filenames of a directory and its subdirectories.

I have a folder named "folder1" that contains the following files:

-rw-r--r--  1 macair  staff    0 Apr 13 16:19 #ababa.txt
-rw-r--r--  1 macair  staff    0 Apr 13 12:51 @babba.txt
drwxr-xr-x  3 macair  staff   96 Apr 24 15:55 Folder2
-rwxr-xr-x  1 macair  staff  379 Apr 25 11:17 myScript
-rw-r--r--  1 macair  staff    0 Apr 13 16:19 test1.txt

and inside "folder2", I have the following file:

-rw-r--r--  1 macair  staff  0 Apr 24 15:55 #cdcda.txt

I tried running a script to change the filenames, but the changes are only being reflected in "folder1" and not in "folder2".

Here's the script I used:

#!/bin/bash

#find . -type f -exec  grep -i "[^0-9a-z/.\/-\_]" {} \;

for FILE in $(find . -type f -exec basename {} \; | grep -i "[^0-9a-z\.\/_\-]")
do
   echo $FILE
   mv "$FILE" "${FILE/[!0-9a-zA-Z.-]/_}"
   
done

echo Done!!

but I received the following error:

mv: rename #cdcda.txt to _cdcda.txt: No such file or directory

As a beginner in Linux, I would appreciate any help or suggestions to improve the code above.

2

There are 2 best solutions below

0
Armali On

You did -exec basename {} presumably to avoid replacing the directory separator /, but thereby you are losing directory information, so don't. When leaving the path intact, we just have to include the / in the class of normal characters:

…find . -type f | …
   mv "$FILE" "${FILE/[!0-9a-zA-Z.\/-]/_}"
0
Andreas Violaris On

Linux is known for providing powerful and efficient utilities that enable users to perform common tasks such as renaming files in straightforward ways without the need for Bash scripts.

With that being said, below you will find a relevant example:

find . -depth -type f -name '*[^0-9a-zA-Z\.\/_\-]*' -exec rename -n 's/[^0-9a-zA-Z\.\/_\-]/_/g' {} +

Result:

rename(./folder2/#cdcda.txt, ./folder2/_cdcda.txt)
rename(./@babba.txt, ./_babba.txt)
rename(./#ababa.txt, ./_ababa.txt)

Notes:

  • depth specifies that subdirectories will be processed before their parent directories.
  • type f specifies that only files will be searched.
  • exec rename specifies that find will execute the rename command on each file that matches the search criteria.
  • s/ specifies a substitution operation.
  • /_/ is the replacement string that replaces any matched character with an underscore.
  • g modifier means global and indicates that all occurrences of the matched characters should be replaced.
  • {} is a placeholder that find will replace with the path to each file that matches the search criteria.
  • + instructs find to execute the rename command on as many files as possible at once, rather than one at a time. This can improve the efficiency of the operation by reducing the overhead of invoking the rename command multiple times.
  • The regular expression pattern you provided was matching uppercase letters, so I updated it to exclude them.

Warning:

  • When performing a mass operation such as renaming multiple files, it's a good practice to preview the results before executing the command. You can accomplish this by using the -n (no action) flag after the rename command. This will simulate the renaming operation and display what the resulting file names will be without actually renaming the files.