I've got a need to recursively rename several thousand files on a CentOS 7 machine currently in the format of:

(32-64 alphanumerics)_various special characters.wav

In short, I would like to recursively remove the _ up to the .wav extension from all files 3 levels deep (NOTE: All extensions are .wav)

Example: Existing:

/home/user/recordings/TEST/december/157A757989F047C3AC2EC3499B4D04A5_7075276189 by [email protected] @ 8_44_52 AM.wav

/home/user/recordings/test/october/28D1E1840DE24D9DB4CF6D1A707533F6337C2DDE_2018690759 by [email protected] @ 1_26_55 PM.wav




I've searched a great deal and have seen many ways to modify other parts of the filename but not retain everything before a specific character (_) and the extension.

Thanks in advance for any assistance.

3 Answers 3


You have to extract the directory part, the file name and then the chars preceding the first underscore (since all files are .wav there's no point to save the extension in a separate variable):

find /home/scott/ -maxdepth 4 -type f -name '*_*.wav' -exec sh -c 'h=${1%/*}
t=${1##*/}; echo mv "$1" "${h}/${t%%_*}".wav' doom {} \;

remove the echo to actually rename the files.


I've searched a great deal and have seen many ways to modify other parts of the filename but not retain everything before a specific character (_) and the extension.

This sed seems to do the trick: sed -E 's/(.*)_[0-9].* by .*(\.wav)/\1\2/'

echo "/home/user/recordings/TEST/december/157A757989F047C3AC2EC3499B4D04A5_7075276189 by [email protected] @ 8_44_52 AM.wav" | sed -E 's/(.*)_[0-9].* by .*(\.wav)/\1\2/'

Testting on your other example string:

echo "/home/user/recordings/test/october/28D1E1840DE24D9DB4CF6D1A707533F6337C2DDE_2018690759 by [email protected] @ 1_26_55 PM.wav" | sed -E 's/(.*)_[0-9].* by .*(\.wav)/\1\2/'

With sed, you save parts of your expression with (), and then put them back with \1\2.

  • Thanks, I was checking the sed man and thinking there would be a way. I'll check this in my test environment. I really appreciate the response. This is for a few thousand files in several hundred directories. Will your example only do one directory at a time or will it do it recursively?
    – Scott B
    Dec 19, 2016 at 18:26
  • This is for one file. You have to implement this with find or something similar to do it recursively,
    – anon
    Dec 19, 2016 at 18:27
  • I have find working but cannot get it to get the results into sed: codefind /home/scott/ .wav -maxdepth 4 -type f -exec sed -E 's/(.)_[0-9].* by .*(\.wav)/\1\2/';code
    – Scott B
    Dec 19, 2016 at 20:12
  • @don_crissti _ not sure how to edit_ That was much closer! But it produced some unexpected changes in my test environment. Within the sub-directories MOST of the files were renamed as I had hoped for. Here is an example of success: File: 1678FEF2B0C040B4890F537365345491_3524474135 by [email protected] @ 6_16_28 AM.wav Ended up: 1678FEF2B0C040B4890F537365345491.wav Here is an example of undesired result: File: 1606D9D38D4F4F639BD87D63AFED8194_8316440833 by @ 7_48_05 AM_Transfer to TMS.wav Ended up: 1606D9D38D4F4F639BD87D63AFED8194_8316440833 by @ 7.wav
    – Scott B
    Dec 19, 2016 at 22:43
  • So...multiple underscores in both success and fail. This is gonna be a tough one. If I HAVE to I'll go in and find the couple hundred fails and manually rename them. I wish I had more time before this project is due... it a great learning experience just trying to think about how to make it happen.
    – Scott B
    Dec 20, 2016 at 0:15

Install any of the prename (Perl rename) implementations, for example File::Rename or the one from Unicode::Tussle. (Last I looked, CentOS didn't come with any Perl rename, only with the util-linux rename which can't do what you need, but this may have changed in CentOS 7.) All of them have the basic syntax prename CODE FILENAME... where CODE is some Perl code that is applied to transform each file name.

find /home/user/recordings/ -type f -exec prename 's!_[^/]*(\.[^./]*)$!$1!' {} +

Explanation for the Perl: (\.[^./]*)$ matches the extension at the end of the name; _[^/]* matches everything in the file name without the directory part (no slashes) starting from the first underscore. The replacement is $, i.e. the part in the first and only parenthesized group.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .