4

I have a lot of files in many directories that are wrongly spelled.

They are spelled Localisation.json and I want to change them to Localization.json.

I tried this, but it does not work:

find -type f -name Localis* | xargs mv Localization.json
mook765
  • 18,644

2 Answers2

13

Notice

First, find -type f -name Localis* | xargs mv Localization.json will never work ... You need something like xargs -I {} mv {} Localization.json or set the xargs's option -n 1.

Second, mv is not the right choice here as it will result in all files named e.g. Localisation.json(or whatever files find matches and outputs) in all sub-directories of the current working directory moved to the current working directory and renamed as Localization.json overwriting this file every time ... e.g. find might output something like ./subdirectory/Localisation.json and that will translate with mv like so:

mv ./subdirectory/Localisation.json Localization.json

changing destination directory to the current working directory ... You, definitely, don't want that.

Therefore, ...

Suggestion

You can format find's output to be NULL delimited(To try to avoid breaking filenames containing e.g. whitespace) with -print0 then pipe it to xargs -0 with rename like so:

find -type f -name "Localisation.json" -print0 | xargs -0 rename -n 's/Localisation\.json$/Localization.json/'

Or. even better, and comparatively efficient ... you can use find's own -exec with rename like so:

find -type f -name "Localisation.json" -exec rename -n 's/Localisation\.json$/Localization.json/' {} \+

and it's important to quote the argument to -name e.g. -name "Localis*"

Notice that everything in the CurrentName part of rename's 's/CurrentName/NewName/' is considered a regular expression ... Hence the escaped dot \. to match a literal . and the $ to match the end of the line excluding directories from being renamed in case you have a parent directory with the same filename e.g. Localisation.json/Localisation.json(That can also be achieved with the rename's option --nopath)

The rename's option -n will only simulate renaming ... Remove it when satisfied with the output to do the actual renaming.

Workaround

You can use find's -execdir(Which runs the specified command from the subdirectory containing the matched file) with mv like so:

find -type f -name "Localisation.json" -execdir echo mv -n -- {} Localization.json \;

You can, also, use find's -exec with mv in a bash command string utilizing bash's parameter expansion and achieve the same result like so:

find -type f -name "Localisation.json" -exec bash -c 'echo mv -n -- "$1" "${1/%Localisation.json/Localization.json}"' _ {} \;

That will do only a renaming dry-run(simulation) ... Remove echo when satisfied with output to do the actual renaming.

Alternatively in bash, you can use recursive shell globbing and parameter expansion with only mv to achieve the same result like so:

shopt -s globstar; for f in **/Localisation.json; do
    # Renaming dry-run(simulation) ... Remove "echo" when satisfied with output to do the actual renaming.
    echo mv -n -- "$f" "${f/%Localisation.json/Localization.json}"
done; shopt -u globstar
Raffa
  • 34,963
4

In most cases, the easiest and most efficient way to rename files is by the command rename. To do it recursively within Bash shell you need to enable the globstar option:

shopt -s globstar

Then you can use the rename command in this way:

rename -n --nopath 's/Localisation\.json/Localization.json/' **/*
  • Remove the -n option to disable the dry-run mode and do the actual rename.

You might want to disable the globstar option afterwards for good measures with:

shopt -u globstar

The above rename command should work out of the box (without enabling additional options) in Zsh shell.

However, although less efficient this way as mv must be invoked for every single argument, you can pipe find's output to xargs with mv in a sh command string like so:

find -type f -name 'Localisation.json' -print0 | xargs -0 -I {} -- sh -c 'echo mv -n -- "$1" $(dirname "$1")/Localization.json' _ {}

echo is for dry-run only. Remove echo when satisfied with the output to do the actual renaming.

Raffa
  • 34,963
pa4080
  • 30,621