Search code examples
regexbashosx-mountain-lion

Is there a pure bash way to eliminate some external commands in my use case?


I'm running OS X 10.8.5 so the rename command is not an option and what I've coded fits in with what the rest of the bash script is doing so I'd prefer to stay within this framework but modify it a bit, if possible. I am however using a later version of bash then the OS X 10.8.5 shipping default, I'm using GNU bash 4.3.30 that I installed from source code. (Yes I know I'm a few patches behind.)

What I'd like to do in the function below, if possible, is to replace the mv's command target argument which is using command substitution with something totaly in pure bash. More specifically I'd like to eliminate the use of pipes and external executables.

Question: Can the command substitution I'm using either be modified or replaced with filename expansion and or pattern matching not using pipes or external executables to achieve my stated goal? (Or something else more reasonable.)

  • I've read the three related sections in the Bash Reference Manual however I have to admit I didn't readily see how it might be possible. Although I did try recoding it a few times however the output was not at all what I wanted, it was either mangled filenames or the error ... bad substitution.

  • All target filenames in the working directory will either already match RegEx.$ext or will be RegEx+foobar.$ext and the way I've coded the function and using mv is if it's not already matching the RegEx it renames the target filenames from RegEx+foobar.$ext to RegEx.$ext. All filenames when truncated will be unique, so there is no need to worry about overwriting an existing RegEx.$ext filename and hence no additional error checking needed.

Example Filenames: filenameC01P01.png filenameC01P02foobar.jpg filenameC01P03.tif

Expected Output: filenameC01P02foobar.jpg -> filenameC01P02.jpg

Code:

function truncate_filenames () {
  for f in *.png *.jpg *.tif; do
      if [[ -f $f ]]; then
          ext="${f:(-3)}"
          if [[ ! ${f%.*} =~ .*C[0-9]{2}P[0-9]{2}$ ]]; then
              mv -v "$f" "$(echo "$f" | grep -Eo '.*C[0-9]{2}P[0-9]{2}').$ext"
          fi
      fi
  done
}

Solution

  • It appears you just want to strip anything following the CxxPxx template, but preserve the extension.

    # Capture both the good part and the trailing garbage
    [[ ${f%.*} =~ (.*C[0-9]{2}P[0-9]{2})(.*)$ ]]
    if [[ -n ${BASH_REMATCH[2]} ]]; then
        mv -v "$f" "${BASH_REMATCH[1]}.$ext"
    fi
    

    You can probably capture the extension in the regular expression as well, eliminating the need to extract it separately.

    [[ $f =~ (.*C[0-9]{2}P[0-9]{2})(.*)\.(.*)$ ]]
    if [[ -n ${BASH_REMATCH[2]} ]]; then
        mv -v "$f" "${BASH_REMATCH[1]}.${BASH_REMATCH[3]}"
    fi