5

I am trying to write a bash shell script in Mac OS X 10.6 (which is called by an Automator Action) to rename TV show DVD rips that I have named badly over the years. I want to remove part of the text in the filenames. I want to remove the text that appears after a specific series of characters that I know will always appear in the filename. But I do not know how many characters will appear before or after the known series of characters. I also don't know if the before or after text will contain multiple periods or dashes. An example would probably help:

The.Big.Bang.Theory.S01E01.xxxxxxxxxxx.mp4

I know that each file will always contain a string in the format of SxxExx where the x's are always numbers. But I do not know what the numbers will be. I want to get the filename up to and including the SxxExx string and the file extension but strip out everything else. So for the above example I would end-up with:

The.Big.Bang.Theory.S01E01.mp4

I have tried using bash's built-in string replacement commands. I thought the expr index command would give me the start point of the SxxExx string and then I could use ${filename:offset:length} to extract only the required part of the filename (I already know the extension so that can be re-added). But it seems the OS X version of expr doesn't include the index functionality. I have only scripted in Basic and LotusScript before. In those environments this would have been fairly easy using commands such as 'Like' and 'Instr' or 'Mid'. But in bash I just can't figure it out. I have spent hours googling trying to understand how to use regular expressions to locate the 'SxxExx' substring in the filename but I just can't figure it out. I hope some clever UNIX scripters will be able to help me!

2

2 Answers 2

5

Try this:

newname=`echo "$filename" | sed -e 's/\(S[0-9][0-9]E[0-9][0-9]\).*\.mp4/\1.mp4/'`

The regular expression is:

  • start a group ( \( )
  • match SXXEXX where X is a numeral between 0 and 9
  • end group ( \) )
  • match any number of any character (except a newline)
  • match a explicit string ( .mp4 )

In the replacement expression:

  • replace with string matched in first group ( \1 )
  • replace with explicit string ( .mp4 )
2
  • Thanks. That worked perfectly. Thank you so much. I knew the sed command would be the key but I just couldn't understand how to use it, despite reading several 'beginners' guides to sed. Thanks again. Are 'S' and 'E' case sensitive? If so, how would I allow it to match either upper or lower case - e.g. Ss or Ee?
    – Stu
    Commented Aug 22, 2011 at 12:43
  • 1
    @Stu, use \([sS][0-9][0-9][eE][0-9][0-9]\) Commented Aug 22, 2011 at 14:02
4

Supposing you have a list of filenames with paths in the file-list file, try the following

while IFS= read -r path; do
  newpath=$(printf '%s\n' "$path" |
    sed 's/\(.*S[0-9]*E[0-9]*\.\).*\.\([^.]*\)$/\1\2/')
  echo mv -- "$path" "$newpath"
done <file-list

Meaning of sed regular expression used, also compared with example input "The.Big.Bang.Theory.S01E01.xxxxxxxxxxx.mp4"

\(                               start of group 1
.*        The.Big.Bang.Theory.   any sequence of characters
S[0-9]*   S01                    a capital S followed by 0 or more digits
E[0-9]*   E01                    a capital E followed by 0 or more digits
\.        .                      a dot
\)                               end of group 1
.*        xxxxxxxxxxx            any sequence of characters
\.        .                      a dot
\(                               start of group 2
[^.]*     mp4                    a sequence of 0 or more non-dot characters
\)                               end of group 2
$                                end of line

In the output, the string \1\2 means "output group1 followed by group2 (so to remove xxxxxxxxxxx.)

Remove the echo if satisfied.

2
  • Thank you. I tried Arcege's suggestion because I saw it first but I think this would work just as well. Thanks so much for your quick response. I really appreciate.
    – Stu
    Commented Aug 22, 2011 at 12:44
  • 1
    @stu: You can use S[0-9]{2}E[0-9]{2} instead of S[0-9]*E[0-9]* if you want to match exactly 2 digits after the S and E.
    – Peter.O
    Commented Aug 22, 2011 at 12:49

You must log in to answer this question.

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