Originally published November 17, 2019 @ 11:39 am

If you google how to append, say, current timestamp to the filename in Bash, almost every suggestion boils down to something really basic along the lines of mv $somefile $somefile_$(date +'Y%-%m-%d'). Technically, this is correct and, yet, this is not what was needed.

Most filenames have extensions that visually identify the files’ type and purpose. If you append a date to the end of the filename, you will effectively remove the filename extension. This is impractical, annoying, and can break things, as illustrated below.

tail -1000 /var/log/messages > /tmp/testfile.log
gzip /tmp/testfile.log
mv /tmp/testfile.log.gz /tmp/testfile.log.gz_$(date +'%Y-%m-%d')
gzip -d /tmp/testfile.log.gz_$(date +'%Y-%m-%d')

gzip: /tmp/testfile.log.gz_2019-11-17: unknown suffix -- ignored

What I really wanted was a new filename looking something like this: /tmp/testfile_2019-11-17.log.gz

Let’s imagine we have a file called unix_server_inventory.xlsx and we want to make a copy of it with the latest modification date appended to the filename. We want to preserve the .xlsx extension. But we also want the same command to work even if the filename has no extension. And here it is:

/bin/cp -p "${f}" "${f%.*}_$(date -d @$(stat -c %Y "${f}") +'%Y-%m-%d')$([[ "${f}" = *.* ]] && echo ".${f##*.}" || echo '')"

ls unix_server_inventory*


Here’s a similar example but the filename now comes with full path and the copy is created in the original directory with the last modification timestamp appended.


/bin/cp -p "${f}" "$(dirname "${f}")/$(basename -- "${f%.*}")_$(date -d @$(stat -c %Y "${f}") +'%Y-%m-%d')$([[ "${f}" = *.* ]] && echo ".${f##*.}" || echo '')"

And another example of renaming multiple files, also by appending the last modification timestamp.

find ${source_path} -mindepth 1 -maxdepth 1 -type f -name "netapp_unix_clients*\.txt" -exec sh -c 'for f; do /bin/cp -p "${f}" "${target_path}/$(basename -- ${f%.*})_$(date -d @$(stat -c %Y ${f}) +'%Y-%m-%d')$([[ "${f}" = *.* ]] && echo ".${f##*.}" || echo '')"; done' sh {} +

Trying to remember the syntax of these commands is probably impractical. A better solution would be to write a small helper script. Perhaps something like this:

if [ -z "${f}" ] || [ ! -f "${f}" ]; then exit 1; fi
/bin/cp -p "${f}" "$(dirname "${f}")/$(basename -- "${f%.*}")_$(date -d @$(stat -c %Y "${f}") +'%Y-%m-%d')$([[ "${f}" = *.* ]] && echo ".${f##*.}" || echo '')"

Save it as /var/adm/bin/cpr.sh and then chmod 755 /var/adm/bin/cpr.sh && ln -s /var/adm/bin/cpr.sh /usr/bin/cpr. Syntax would be very basic:

[root@ncc1711:~] # touch "/tmp/igor 1.txt"
[root@ncc1711:~] # cpr /tmp/igor 1.txt
[root@ncc1711:~] # ls -als /tmp | grep igor
 0 -rw-r--r--   1 root     root         0 Nov 17 11:21 igor 1_2019-11-17.txt
 0 -rw-r--r--   1 root     root         0 Nov 17 11:21 igor 1.txt