Nips
Nips

Reputation: 13890

How to change file and directory names with find?

I changed project name and now I have many files an directories with old name. How to replace these names with find?

find . -name "*old_name*" -exec ???

Upvotes: 5

Views: 1622

Answers (6)

Łukasz Rajchel
Łukasz Rajchel

Reputation: 331

renaming multiple directories with find: how to catch the catches

here's a little story on how to rename multiple files with find, but for the impatient I first put the proper command for your specific problem here:

find -depth -name "old name" -execdir mv -iv {} "new name" \;

and for the patient ones, a few misadventures with find, restricting changes to directories only:

  • we want to rename all directories named old dir into new dir recursively inside a current directory

    • we create an empty directory inside which we create a simple directory hierarchy:

      $ cd $(mktemp -d) && mkdir -p "old dir" "subdir/old dir/old dir"
      $ find | sort
      .
      ./old dir
      ./subdir
      ./subdir/old dir
      ./subdir/old dir/old dir
      
    • the use of of the -exec action works only for a rename directly under a current directory, which you can see by dry-running a rename command (note that the shell doesn't output the quotes, which is not an error):

      $ find -type d -name "old dir" -exec echo mv -iv {} "new dir" \;
      mv -iv ./subdir/old dir new dir
      mv -iv ./subdir/old dir/old dir new dir
      mv -iv ./old dir new dir
      

      note that only the first rename command would work as expected

    • the use of the -execdir action runs the command from the subdirectory containing the matched directory:

      $ find -type d -name "old dir" -execdir echo mv -iv {} "new dir" \;
      mv -iv ./old dir new dir
      mv -iv ./old dir new dir
      mv -iv ./old dir new dir
      

      which seems fine as the rename commands are run in the matching directories, so we no longer dry-run:

      $ find -type d -name "old dir" -execdir mv -iv {} "new dir" \;
      './old dir' -> 'new dir'
      find: ‘./subdir/old dir’: No such file or directory
      './old dir' -> 'new dir'
      find: ‘./old dir’: No such file or directory
      $ find | sort
      .
      ./new dir
      ./subdir
      ./subdir/new dir
      ./subdir/new dir/old dir
      
      

      the problem is that the old dir is renamed new dir and find cannot descend further inside a renamed directory

    • a solution is to process each directory's contents before the directory itself, which is precisely what the -depth option does:

      $ cd $(mktemp -d) && mkdir -p "old dir" "subdir/old dir/old dir"
      $ find -depth -type d -name "old dir" -execdir mv -iv {} "new dir" \;
      './old dir' -> 'new dir'
      './old dir' -> 'new dir'
      './old dir' -> 'new dir'
      $ find | sort
      .
      ./new dir
      ./subdir
      ./subdir/new dir
      ./subdir/new dir/new dir
      

Upvotes: 0

Josh
Josh

Reputation: 401

Below is what I have used in the past. The biggest gotcha is the RHEL rename (c) vs Debian rename (perl) - They take different options. The example below uses RHEL c based rename command. Remove the '-type f' to also rename the directories.

find . -type f -name "*old_name*" -print0 | xargs -0 -I {} /usr/bin/rename "old_name" "new_name" {}

Upvotes: 1

南山竹
南山竹

Reputation: 524

1. First, backup your directories and files

The following Bash code run on my OS X and Ubuntu boxes.

2. Rename the directories from old_dir to new_dir:

for d in $(find . -maxdepath X -type d -name 'old_dir'); do mv $d "$(dirname $d)/new_dir"; done

X is a number used to specify the depth of replacing old_dir

3. Rename the files from old_file to new_file:

for f in $(find . -type f -name 'old_file'); do mv $f "$(dirname $f)/new_file"; done
  1. Don't care about @Benjamin W. and @ghoti them one for his ForMatted code and @ghoti try to orient the question to his Pitfalls.

Hi, @Benjamin W. what about this new post? and @ghoti did you run the above code incorrectly on your machine? If the code can't work just let me know or post a question pls, and if you had a better one pls post here let we know.

Upvotes: -1

anubhava
anubhava

Reputation: 786291

This find should work for you:

find . -name "old_name" -execdir mv "{}" new_name +

This will find files with the name old_name from the current dir in all sub directories and rename them to new_name.

Upvotes: 2

Nips
Nips

Reputation: 13890

Ok, my solution:

find . -name "*old_name*" -exec rename 's/old_name/new_name/g' {} \;

But this works for directories which name not contain "old_name", otherwise find say for example:

find: `./old_name': No such file or directory

Because it trying search in "old_name" directory, and the directory is already a "new_name"

Upvotes: 0

V H
V H

Reputation: 8587

newname="myfile.sh"; for files in $(find Doc2/scripts/ -name gw_watch_err.sh); do echo $files; dir=${files%/*}; cfile=${files##*/}; echo "$dir -- $cfile";  echo "mv $cfile $newname";  done
Doc2/scripts/gateway/gw_watch_err.sh
Doc2/scripts/gateway -- gw_watch_err.sh
mv gw_watch_err.sh myfile.sh

you could also add:

find . -maxdepth 1 -iname file

where maxdepth will ensure you dont need to worry about sub folders and iname means case sensitive

Upvotes: 0

Related Questions