1

I have multiple files in multiple directories and i have to rename these files from lowercase to uppercase; the file extension may vary and needs to be in lowercase (should be renamed too for files with extensions in uppercase).

NB: I have rename version from util-linux on CentOS Linux7.

i tried this :

find /mydir -depth | xargs -n 1 rename -v 's/(.*)\/([^\/]*)/$1\/\U$2/' {} \;
find /mydir -depth | xargs -n 1 rename -v 's/(.*)\/([^\/]*)/$2\/\L$2/' {} \;

but it's not working it changes nothing and i have no output.

Itried another solution :

for SRC in `find my_root_dir -depth`
do
   DST=`dirname "${SRC}"`/`basename "${SRC}" | tr '[A-Z]' '[a-z]'`
   if [ "${SRC}" != "${DST}" ]
   then
      [ ! -e "${DST}" ] && mv -T "${SRC}" "${DST}" || echo "${SRC} was not renamed"
   fi
done

this one partially works but transforms the files extensions to uppercase too.

Any suggestions on how to keep/transform the extensions to lowercase ?

Thank you!

TenEM
  • 127
  • 1
  • 8
  • 1
    The trailing `{} \;` seems to indicate that you might have confused the syntax of `find`'s `-exec` action with `xargs`. There are two versions of the `rename` command with different syntax. See e.g. https://stackoverflow.com/q/31408764/10622916 and https://stackoverflow.com/a/1086509/10622916. Maybe you have the util-linux version instead of the Perl version. – Bodo May 18 '22 at 16:14
  • yes i have the util-linux version 2.23.2 – TenEM May 18 '22 at 16:33
  • 1
    The util-linux version cannot use the substitution command, it has a simpler syntax, see https://linux.die.net/man/1/rename. The Perl version might be available with a different name. What Linux distribution and version do you use? Please [edit] your question and add all information to the question. – Bodo May 18 '22 at 17:01
  • @Bodo i edited the question adding the rename version and the distribution ihave – TenEM May 18 '22 at 17:08
  • Don't use `for x in $(find ...)`. See https://mywiki.wooledge.org/BashFAQ/001 and https://mywiki.wooledge.org/UsingFind – Bodo May 18 '22 at 19:11

3 Answers3

1

rename-independent solution (using find together with mv)

You can rename all files in a directory with a following command:

for i in $( ls | grep [A-Z] ); do mv -i $i `echo $i | tr 'A-Z' 'a-z'`; done

First part (for i in $( ls | grep [A-Z] );) looks for all uppercase characters and executes until all files are "scanned".

Second part (``) turns all uppercase characters into lowercase ones.


Perl-based rename dependent solution

rename -f 'y/A-Z/a-z/' *

This command changes uppercase characters to the lowercase ones. -f option allows overwriting of existing files, but it is not necessary.

Kaper365
  • 355
  • 3
  • 14
  • i tried to use the first suggestion using mv but i get an error `mv: file1.ext and FILE1.EXT are the same file` and the issue here is that it transforms even the file extension. – TenEM May 18 '22 at 17:20
  • @TenEM Do you use a case-insensitive file system? Or Windows? – Bodo May 18 '22 at 19:13
1

Possible solution with Perl rename:

find /mydir -depth -type f -exec rename -v 's/(.*\/)?([^.]*)/$1\U$2/' {} +

The commands in the question have several problems.

You seem to confuse the syntax of find's -exec action and xargs.

find /mydir -depth -type f -exec rename -v 'substitution_command' {} \;
find /mydir -depth -type f| xargs -n 1 rename -v 'substitution_command'

The xargs version has problems in case a file name contains a space.

If you replace \; with +, multiple file names are passed to one invocation of rename.


The substitution command is only supported by the Perl version of the rename command. You might have to install this version. See Get the Perl rename utility instead of the built-in rename


The substitution did not work in my test. I successfully used

rename -v 's/(.*\/)?([^.]*)/$1\U$2/' file ...

The first group (.*\/)? optionally matches a sequence of characters with a trailing /. This is used to copy the directory unchanged.

The second group ([^.]*) matches a sequence of characters except ..

This is the file name part before the first dot (if any) which will be converted to uppercase. In case the file name has more than one extension, all will remain unchanged, e.g.
Path/To/Foo.Bar.Baz -> Path/To/FOO.Bar.Baz

Bodo
  • 9,287
  • 1
  • 13
  • 29
0

suggesting a trick with awk that will generate all required mv commands:

awk '{f=$0;split($NF,a,".");$NF=tolower(a[1])"."toupper(a[2]);print "mv "f" "$0}' FS=/ OFS=/ <<< $(find . -type f)

Inspect the result, and run all mv commands together:

bash <<< $(awk '{f=$0;split($NF,a,".");$NF=tolower(a[1])"."toupper(a[2]);print "mv "f" "$0}' FS=/ OFS=/ <<< $(find . -type f))

awk script script.awk explanation

BEGIN { # preprocessing configuration
  FS="/"; # set awk field separtor to /
  OFS="/"; # set awk output field separtor to /
}
{ # for each line in input list
  filePath = $0; # save the whole filePath in variable
  # fileName is contained in last field $NF
  # split fileName by "." to head: splitedFileNameArr[1] and tail: splitedFileNameArr[2]
  split($NF,splitedFileNameArr,"."); 
  # recreate fileName from lowercase(head) "." uppercase(tail)
  $NF = tolower(splitedFileNameArr[1]) "." toupper(splitedFileNameArr[2]);
  # generate a "mv" command from original filePath and regenerated fileName
  print "mv "filePath" "$0; 
}

Testing:

mkdir {a1,B2}/{A1,b2} -p; touch {a1,B2}/{A1,b2}/{A,b}{b,C}.{c,D}{d,C}
find . -type f

./a1/A1/Ab.cC
./a1/A1/Ab.cd
./a1/A1/Ab.DC
./a1/A1/Ab.Dd
./B2/b2/AC.DC
./B2/b2/AC.Dd
.....
./B2/b2/bC.cd
./B2/b2/bC.DC
./B2/b2/bC.Dd

awk -f script.awk <<< $(find . -type f)

.....
mv ./a1/b2/Ab.cd ./a1/b2/ab.CD
mv ./a1/b2/Ab.DC ./a1/b2/ab.DC
mv ./a1/b2/Ab.Dd ./a1/b2/ab.DD
mv ./B2/A1/bC.Dd ./B2/A1/bc.DD
.....
mv ./B2/b2/bC.DC ./B2/b2/bc.DC
mv ./B2/b2/bC.Dd ./B2/b2/bc.DD

bash <<< $(awk -f script.awk <<< $(find . -type f))
find . -type f
Dudi Boy
  • 4,551
  • 1
  • 15
  • 30