Find and replace last char '_' with '.' on filenames recursively
It's replacing all instances of _
because ${1//_/.}
is global (${1/_/.}
would be non-global, but replace the first match rather than the last).
Instead you could use POSIX ${1%_*}
and ${1##*_}
to remove the shortest suffix and longest prefix, then rejoin them:
find . -name '*_pdf' -type f -exec sh -c 'mv "$1" "${1%_*}.${1##*_}"' sh {} \;
or
find . -name '*_pdf' -type f -exec sh -c 'for f do mv "$f" "${f%_*}.${f##*_}"; done' sh {} +
For multiple extensions:
find . \( -name '*_pdf' -o -name '*_jpg' -o -name '*_jpeg' \) -type f -exec sh -c '
for f do mv "$f" "${f%_*}.${f##*_}"; done
' sh {} +
I removed the --
end-of-options delimiter - it shouldn't be necessary here since find
prefixes the names with ./
.
You may want to add a -i
option to mv
if there's risk that both a file_pdf
and file.pdf
exist in a given directory and you want to be given a chance not to clobber the exising file.pdf
.
With perl rename
tool:
find ... rename 's/_pdf$/.pdf/' {} +
You can run that individually for your different extensions, or replace multiple "common extensions" at once:
find ... rename 's/_(pdf|jpg|jpeg)$/.\1/' {} +
If you don't have perl rename, you can use other rename
tool:
find ... rename '_pdf' '.pdf' {} +
The obligatory zsh
version:
autoload zmv # best in ~/.zshrc
zmv -v '(**/)(*)_(pdf|jpg|jpeg)(#q.)' '$1$2.$3'
((#q.)
being to restrict to regular files as find
's -type f
does. Change to (#qD.)
if you also want to rename hidden files or files in hidden directories like find
does. Replace -v
with -n
for a dry-run).