I have hundreds of folders with thousands subdirectories and files inside of them. The file names are currently in the format

sf{number}-{number} - {text}

and I need everything deleted until {text}.


sf012-05 - toto - africa.cdg 
sf012-05 - toto - africa.mp3


toto - africa.cdg
toto - africa.mp3

Additional formats:

SF-108 - 02 Johnny Hates Jazz Shattered Dreams[K].cdg
02 - David Essex - Rock On.cdg
SF0504 - 13 Walker Brothers, The  Make It Easy On Yourself.cdg
SFLY 051 - 07 America  A Horse With No Name.cdg
SF217-12 this love - maroon 5.cdg
SF262-13. Same Mistake - James Blunt.cdg
SF287 - 13 Luke Bryan Do I.cdg
SF355-01-15 - Tough Love - Pony.cdg
Track 11 Livin' La Vida Loca (Radio Edit) - Martin, Ricky.cdg
Sunfly Hits 275 - 07 SF275-7-Right Now (Na Na Na)-Akon.mp3
Second, Minute Or Hour - Jack Penate.mp3
SF057 - Karaoke Classics Vol.57 - 03 Abba Thank You For The Music.cdg

especially the last one is total crap.

That means I need a recursive regex bash script or something similar to go through these files and to rename them.

  • Please be more specific with an accual file name. – fd0 May 20 '16 at 17:11
  • sf012-05 - toto - africa.cdg sf012-05 - toto - africa.mp3 sf225-14 - duran duran - wild boys.cdg sf225-14 - duran duran - wild boys.mp3 – Pulz May 20 '16 at 17:12
  • What happens if two different files have the same {text}? – Law29 May 20 '16 at 17:12
  • Well two files are always going to have the same name since it's karaoke music, one is in cdg format and the other in mp3. they should all stay. – Pulz May 20 '16 at 17:14
  • So in this case you want to remove sf012-05 - and keep, say, toto - africa.cdg? – Law29 May 20 '16 at 17:14

The easiest tool here would be zsh (like bash, only better), with its fancy globbing and the zmv function. Run this from the toplevel directory where you want to rename files.

autoload -U zmv
zmv '(**/)[Ss][Ff][0-9]##-[0-9]##[- ]#(*)' '$1$2'

Explanation: **/ is any string of leading directories. Then there's a pattern of sf (case-insensitive) followed by one or more digits, a dash and another number, followed by dashes and spaces. Finally there can be any suffix *. The numbers in the replacement refer to the parenthesized groups in the pattern.

Alternatively, you can use prename (the Perl-based rename script, available on Debian and Arch Linux, might need to be installed separately on other distributions). You'll need to combine it with find to gather the list of files to rename. It doesn't matter if rename is passed non-matching files, it'll skip files for which there's no renaming to be done.

find -name '[Ss][Ff]*' -exec prename 's!/!sf[0-9]+-[0-9]+[- ]*!!i' {} +

Here is the code (replace directory by the main directory, or where you have the subdirs):

cd directory
find -type f | while IFS= read - file; do 
    name=$(echo "$file"  | rev | cut -d'/' -f1 | rev |  cut -d'-' -f3- | cut -d' ' -f2-) 
    path=$(dirname "$file")  
    echo mv "$file" "$newname" 

Where directory is the main directory where all the subdirectories is....

Please dont remove the "echo" from "mv", test it first.

Be carefully for not run that in the wrong dir, it can be very dangerous.

  • It returns "find: paths must precede expression: ." – Pulz May 20 '16 at 17:49
  • Sorry Remove the '.' from find and it will work. – Luciano Andress Martini May 20 '16 at 17:50
  • I think the hyphen in the middle is not being removed. mv ./SF 003/sf003-03 - queen - bohemian rhapsody.cdg ./SF 003/03 - queen - bohemian rhapsody.cdg – Pulz May 20 '16 at 17:55
  • Change -f2- to -f3-. and see what happens. (updated the post) – Luciano Andress Martini May 20 '16 at 17:56
  • Almost perfect, there's still a whitespace left after the directory though. ./SF 001/ seal - crazy.cdg – Pulz May 20 '16 at 17:59

You say you need everything deleted until {text}, but your example doesn't show that. Rather than showing exact code, let me just suggest a plan.

1) Write a script that does what you want for one filename. Something like this might do:

#! /bin/sh
test -d ~/OK/${PWD} || mkdir -p ~/OK/${PWD}
ln "$1" "~/OK/${PWD}/$1"
echo "$1" | awk -F ' - ' '{print $NF}'

That breaks the name on the pattern ' - ' and prints the last field. And saves the original filename so you can retrieve it if something goes wrong. Test it a few times, and use mv to restore the names.

2) Invoke your script with find:

$ find dirname -exec script.awk {} +

That will bury your machine with many parallel invocations of your script. When it's done, you'll have a bunch of OK directories to remove, and you'll know how to use find to remove them.


If you have the Perl-based rename utility (called prename on some distributions) you can use a Regular Expression to rename all the files in one go.

Your requirement asks for the removal of sf{number}-{number} but all the examples start with capitals, some of them contain only one {number}, {number}-{number}-{number}, or even {number}{space}-{space}{number}. I'm going to assume that you want to include the first two of these cases.

rename -n 's/^sf[0-9-]+\s+(-\s+)?//' [Ss][Ff]*

Remove the -n when you are ready for it to apply its changes , or change it to -v to see it process files as it goes.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.