I want to rename all my folders and folders within these folders, so that all underscores in the folder name are replaced by spaces. Can you help me with that?
The answer that uses -execdir
is simpler, but will only work with versions of find
that support -execdir
such as GNU find, since POSIX only specifies -exec
.
If you just want to use bash, this is surprisingly tricky to get right, since you're renaming the directories that find
is searching through. Even if you get the order right, by using the -depth
option to find
, you need to make sure you only rewrite the last component of the path with each -exec
. The following is a trivial variation of one of the examples given in the Bash FAQ and seems to work OK for me:
find . -depth -name "*_*" -exec bash -c 'dir=${1%/*} base=${1##*/}; mv "$1" "$dir/${base//_/ }"' _ {} \;
That FAQ answer has more discussion about problem of recursively naming folders which may be of interest.
Update: Since that one-liner is rather complex, it might be worth breaking it down a little in the interests of explanation. Essentially, the find
command is:
find . -depth -name "*_*" -exec bash -c [SOME-STUFF] _ {} \;
In other words, find all the directories which contain an underscore, and for each such directory, starting with the deepest, run the bash script [SOME-STUFF]
, with parameter 0 as _
(to indicate that we don't care about it) and parameter 1 as the name of the file that find found. (find
will substitute the filename for {}
after -exec
. The \;
just terminates the command that -exec
runs.)
Then the [SOME-STUFF]
part is made up of:
dir=${1%/*}
... which, using parameter expansion, will remove the shortest match for /*
from the end of $1
(the filename) and set dir
to the result. Similarly, this part:
base=${1##*/}
... removes the longest match of */
from the start of $1
and sets base
to the result. So base
is just the last component of the path.
Then the renaming is actually done by the mv
command, which is:
mv "$1" "$dir/${base//_/ }"
That again uses parameter expansion, this time with the ${parameter/pattern/string}
syntax. The filename ($1
) is renamed to $dir
followed by $base
but with each underscore in $base
replaced with a space.
Yes, simply cd to /dir and :
find -depth -type d -execdir rename 's/_/ /g' {} \;
Depending of the distro, you need perl rename (sometimes prename).
If
file $(type -p rename)
output contains ELF, that looks bad ;)
edit -depth and -execdir added
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With