62

I use this command to find files with a given pattern and then rename them to something else

find . -name '*-GHBAG-*' -exec bash -c 'echo mv $0 ${0/GHBAG/stream-agg}' {} \;

As I run this command, I see some outputs like this

mv ./report-GHBAG-1B ./report-stream-agg-1B
mv ./reoprt-GHBAG-0.5B ./report-stream-agg-0.5B

However at the end, when I run ls, I see the old file names.

mahmood
  • 23,197
  • 49
  • 147
  • 242
  • 2
    A better question for most people who google this is https://stackoverflow.com/questions/1086502/rename-multiple-files-in-unix – tripleee Aug 31 '17 at 07:15

4 Answers4

95

You are echo'ing your 'mv' command, not actually executing it. Change to:

find . -name '*-GHBAG-*' -exec bash -c 'mv $0 ${0/GHBAG/stream-agg}' {} \;
kamituel
  • 34,606
  • 6
  • 81
  • 98
37

I would suggest using the rename command to perform this task. rename renames the filenames supplied according to the rule specified as a Perl regular expression.

In this case, you could use:

rename 's/GHBAG/stream-agg/' *-GHBAG-*
anumi
  • 3,869
  • 2
  • 22
  • 22
  • Thanks. It is much simpler but I was playing with `exec` – mahmood Mar 08 '13 at 09:11
  • Certainly, `find` with `-exec` has the advantage of searching recursively down the directory, which rename cannot. But `rename` will be faster as it will not spawn a process for each individual file to be renamed. – anumi Mar 08 '13 at 09:17
  • 1
    @anumi `man find` shows `-exec command {} +` as option to execute only one instance on all found files. Although, I don't know whether it's useful with the example in original question. – F-3000 Apr 05 '14 at 12:30
  • 2
    @F-3000: The command would be `find . -name '*-GHBAG-*' -exec rename 's/GHBAG/stream-agg/' {} +` and it would do the job admirably, even if the file names contain spaces or newlines. – Jonathan Leffler Sep 03 '14 at 06:42
  • This would be great for small filesets. For handing many many files use 'find' because this (or an 'ls' solution ) wont be able to evaluate the wildcards with so many files. – Ross Oct 31 '14 at 05:42
  • Not sure if it's because I was using another version of rename, I had to use `rename GHBAG stream-agg *-GHBAG-*` – Julian Mar 03 '17 at 17:19
  • @JonathanLeffler Perfect! – xabush Apr 02 '23 at 18:40
20

In reply to anumi's comment, you could in effect search recursively down directories by matching '**':

rename 's/GHBAG/stream-agg/' **/*-GHBAG-*
xnzac
  • 341
  • 2
  • 7
  • 1
    I tried this, and it worked only for one path out of several subdirectories. `find . -name "*:*"` still gave a list of files after running `rename -v 's/:/-/' **/*:*`, and second rename-run gave "_No such file or directory_" notify. (Debian 6) – F-3000 Apr 05 '14 at 11:52
  • 1
    rename is not universal unix command. – Phyo Arkar Lwin Sep 13 '15 at 10:35
2

This works for my needs, replacing all matching files or file types. Be warned, this is a very greedy search

# bashrc
function file_replace() {
  for file in $(find . -type f -name "$1*"); do
    mv $file $(echo "$file" | sed "s/$1/$2/");
  done
}

I will usually run with find . -type f -name "MYSTRING*" in advance to check the matches out before replacing.

For example:

file_replace "Slider.js" "RangeSlider.ts"

renamed:    packages/react-ui-core/src/Form/Slider.js -> packages/react-ui-core/src/Form/RangeSlider.ts
renamed:    stories/examples/Slider.js -> stories/examples/RangeSlider.ts

or ditch the filetype to make it even greedier

file_replace Slider RangeSlider

renamed:    packages/react-ui-core/src/Form/Slider.js -> packages/react-ui-core/src/Form/RangeSlider.js
renamed:    stories/examples/Slider.js -> stories/examples/RangeSlider.js
renamed:    stories/theme/Slider.css -> stories/theme/RangeSlider.css
random-forest-cat
  • 33,652
  • 11
  • 120
  • 99