Shell – Backup file with .bak _before_ filename extension

renameshell

I commonly backup config files on servers without version control like so:

# cp httpd.conf{,.bak}
# ls
httpd.conf    httpd.conf.bak

However, for files in the web root I take the time to carefully put the .bak before the filename extension so that the .php will remain and Apache will still send the file through the PHP interpreter if someone manages to guess or probe the server for such files:

$ cp index.php index.bak.php
$ ls
index.php    index.bak.php

Is there any way to use tab-completion and muscle memory to put the .bak before the filename extension? I have Tab{}←,.bak burned into muscle memory and I cannot use this nice "meat macro" on files in the web root.

Thanks.

Best Answer

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