1

This question already has an answer here:

I want to replace a string with a different string in all directory and file names, recursively. So if I want to replace foo with bar and have this file:

 foo_project/my_app/old_foo/start_foo.sh

I would want it to become

 bar_project/my_app/old_bar/start_bar.sh

I have been able to do this, but it's pretty ugly. Writing it from memory (can't copy paste so forgive typos) the command I have is:

find $PROJECT_DIR -name "*foo*" | tac | xargs -n 1 -I % bash -c "eval mv % \$\( echo % \| sed \''s/\(.*\)foo/\1$PROJECT_NAME/'\' \)"

This works for me, but it's hideous. Is there a cleaner approach to do what I want?

To give context on above command, the issue I had was that early rename commands would break later mv commands by making the paths differ. That's why I switched to a tac before an xargs (instead of the -evalcmd argument for find I started with) to ensure that I ran the most deeply nested renames first. I then had to make my sed only rename the last instance of foo so I could move everything within a folder before renaming the folder.

The ugly part is the need of eval, and thus multiple levels of escaping arguments, because I didn't otherwise know how to pass the contents of xargs to the sed command. There has to be something cleaner then this though?

marked as duplicate by don_crissti, Stephen Harris, PersianGulf, mdpc, slm bash Jul 26 '16 at 7:19

This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.

  • 1
    Please clarify; do you want to rename files and directories, or replace the contends inside files, or both? – Stephen Harris Jul 25 '16 at 18:13
  • @StephenHarris I want to rename the actual file and directory, not the contents within them. – dsollen Jul 25 '16 at 19:18
  • @don_crissti I have looked through quite a few questions. I had thought this would be trivial and already answered, but I've yet to find an answer for this specific use case. The need to rename directories recursively breaks most examples I've seen, because once the parent directory is renamed later commands are unable to find that directory. I created my solution using ideas from rename answers, but had no luck finding someone who did what I needed. – dsollen Jul 25 '16 at 19:20
0

For potentially tricky, and potentially dangerous things like this, I like to generate a script to do what I want.

$ find foo_project -print > /tmp/list
$ vi /tmp/list
:%s/.*/'&'/            # quote all filenames just to be safe
:%s/.*/mv & &/         # generate mv commands
:%s/' '/'\r'/          # break them for the next step
:v/^mv /s/foo/bar/g    # modify the second arguments
:g/^mv /j              # re-join the lines

Now take a moment, examine the script you just created it to make sure it's going to do what you want to do:

mv 'foo_project/my_app/old_foo/start_foo.sh' 'bar_project/my_app/old_bar/start_bar.sh'
...

If you like it:

:wq
$ sh /tmp/list

This is a general technique I use all the time. The exact way you edit the script depends on what you want to accomplish. It's not a sexy one-liner, but it's safe.

Now, if this is something you want to do on a regular basis, and not a one-time-thing, then this technique is probably not what you want. But for a one-shot operation where you're kind of feeling your way through, this works great.

Now, if you want a re-usable solution, it's time to write a script:

#!/bin/sh
find foo_project -print | while read filename; do
  newname=`echo $filename | sed -e 's/foo/bar/g'`
  echo "renaming $filename => $newname"
  mv "$filename" "$newname"
done
1

If you're using CentOS or some system without rename (sometimes known as prename) then the suggested duplicate is a good place to look. If you're on a Debian derived system this may work for you as well:

find "$PROJECT_DIR" -depth -type d -execdir echo rename 's/foo/bar/' {} +

This descends the directory tree depth first and renames all the items in each directory, changing foo to bar in each instance.

(Actually, it doesn't do anything except show you what it would do. Remove echo when you're happy...)

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