Backup file with .bak _before_ filename extension

Might be as simple as

cp file.{ext,bak.ext}

bash {} expansion expands to all combinations (unlike [] expantion which lists existing files. So it generates two file names.

The { .... , .... } provides two options, so you get file.ext and file.bak.ext

In your script you would use

cp $FILE.{$EXTENTION,bak.$EXTENTION}

And to break up the FILENAME into the two halves you can use

FILE=${FILENAME%.*}
EXTN=${FILENAME##*.}

${VAR%xxxxx} will return the left side of what is in VAR up to the matching pattern, in the case above the pattern is .* so you get everything except the last .*

${VAR##xxxxx} will return the right side of what is in VAR, after the matching pattern. The double hash means get the biggest possible match, in case there is more than one possble match.

To make your script save, check for cases where after obtaining FILE and EXTENTION, that they are not the same thing. This will happen in cases where the file name doesn't have an extention.

A sub-section of your script might look like this:

ls *html | while read FILENAME
do
    FILE=${FILENAME%.*}
    EXTN=${FILENAME##$FILE}
    cp $FILE{$EXTN,.bak$EXTN}
done

You can always use a function:

backup()
  for i do
    case ${i##*/} in
      (?*.?*) cp -iv -- "$i" "${i%.*}.bak.${i##*.}";;
      (*) cp -iv -- "$i" "$i.bak"
    esac
  done

${i##*/} is the basename of $i (that is $i with everything up to the rightmost / removed), because we don't want to consider bar/file as the extension in foo.bar/file.

Then we check whether $i contains an extension, that is if it contains at least one dot character with characters on both sides (?* is any one character followed by 0 or more characters, so it's one or more characters). We can't do *.* otherwise we'd backup ~/.bashrc as ~/.bak.bashrc for instance.

That syntax above is POSIX except for the -v (for verbose) option to cp which is a GNU extension also found in a few other implementations like BSDs'.

Then you can do things like:

backup foo.php bar.html baz<Tab>...

Or:

backup *.php

Without having to worry about losing data (-i will ask for confirmation before overwriting a file, see also the -b option with GNU cp) or not picking the right name.


I can't offhand think of a way to do this with tab completion, except possibly to also keep a copy of the file name without the extension, so you can add "bak.php" in one go. But my solution to this would be a small shell script, that takes a file name as input and copies it to "firstpart.bak.extension".

Tags:

Shell

Rename