2

I currently have 2000 user directories and each of these directories have sub directories.

user_1

---> child1

---> child2

user_29

---> child37

---> child56

etc

I need to loop through all of the user folders and then through each of the child folders and rename the child folders with a prefix 'album_'. My end structure should be:

user_1

---> album_child1

---> album_child2

user_29

---> album_child37

---> album_child56

I was able to rename the user folders with the following command:

for f in * ; do mv "$f" user_"$f" ; done;

I have been trying several different approaches to rename the sub directories such as:

find . -maxdepth 2 -mindepth 2 -type d -exec mv '{}' album_'{}'

The first part of the above query returns all the directories that I need to rename ('find . -maxdepth 2 -mindepth 2 -type d').

How do I access the directory name in the -exec function and then append a prefix to it?

1

Try:

find . -maxdepth 2 -mindepth 2 -type d -execdir bash -c 'mv "$1" "./album_${1#./}"' mover {} \;

Notes:

  1. To form the name for the target directory, we need to remove the initial ./ that will be in the directory name. To accomplish that, we use the shell's prefix removal: ${1#./}.

  2. We use -execdir rather than -exec because it is more robust in case directories are renamed while the command runs.

  3. In the expression bash -c '...' mover {}, bash runs the command in single quotes with $0 assigned to mover and $1 assigned to the file name, {}. $0 is unused unless the shell needs to write an error message.

  4. We don't need any bash features for this code. Any POSIX shell could be used.

  5. If you want to test the command before you run it to make sure it does what want, add echo like this:

    find . -maxdepth 2 -mindepth 2 -type d -execdir bash -c 'echo mv "$1" "./album_${1#./}"' mover {} \;
    

Notes regarding -exec mv '{}' album_'{}'

  1. Don't quote the {}. find handles that.

  2. Because the file name provided by find will start with ./, the form album_{} will not succeed.

Example

Let's consider these directories:

$ ls *
user1:
child1  child2

user2:
child1  child2
Press any key to continue...

Now. let's run our command and see the new directory names:

$ find . -maxdepth 2 -mindepth 2 -type d -execdir bash -c 'mv "$1" "./album_${1#./}"' mover {} \;
$ ls *
user1:
album_child1  album_child2

user2:
album_child1  album_child2
  • What about adding a prefix to the sub directories' name? Each sub dir needs to have 'album_' added to it. – Robert Sep 16 '16 at 6:30
  • @Robert Yes, that is what it does. I just added an example showing that is adds album_ to each subdirectory name. Have a look and tell me if you want something different. – John1024 Sep 16 '16 at 6:38
  • 1
    Thanks John - I misread your answer the first time, but re-reading it - I can see that you did exactly as I asked! Your solution worked well. – Robert Sep 16 '16 at 7:16
1

If you have perl based rename

find -maxdepth 2 -mindepth 2 -type d -exec rename -n 's|.*/\K|album_|' {} +
  • .*/ matches upto last / in directory path
  • by using \K lookbehind, we can avoid need of capture group in this case
  • The . in find . can be skipped as find works on current directory by default

Remove -n option from rename once it shows correct renaming

Example:

$ ls *
user_1:
child1  child2

user_29:
child37  child56

$ find -maxdepth 2 -mindepth 2 -type d -exec rename -n 's|.*/\K|album_|' {} +
rename(./user_29/child37, ./user_29/album_child37)
rename(./user_29/child56, ./user_29/album_child56)
rename(./user_1/child2, ./user_1/album_child2)
rename(./user_1/child1, ./user_1/album_child1)
$ find -maxdepth 2 -mindepth 2 -type d -exec rename 's|.*/\K|album_|' {} +

$ ls *
user_1:
album_child1  album_child2

user_29:
album_child37  album_child56
0

I usually don't use -exec directly because it's more difficult to debug if something goes wrong. Just let find write a script which you can review before executing:

find . -maxdepth 2 -mindepth 2 -type d -printf 'mv "%P" "%h/album_%f"\n' > /tmp/rename.sh sh /tmp/rename.sh

0

If we have perl based rename installed,

rename 's!(.*/)!$1album_! if(-d $_)' user*/*
  • It would've been useful if you provided some explanation to this command. – David Apr 4 at 4:41

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.