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:
f=unix_server_inventory.xlsx /bin/cp -p "${f}" "${f%.*}_$(date -d @$(stat -c %Y "${f}") +'%Y-%m-%d')$([[ "${f}" = *.* ]] && echo ".${f##*.}" || echo '')" ls unix_server_inventory* unix_server_inventory.xlsx unix_server_inventory_2019-11-12.xlsx
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.
f=/root/unix_server_inventory.xlsx /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:
#!/bin/bash f="${@}" 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
Experienced Unix/Linux System Administrator with 20-year background in Systems Analysis, Problem Resolution and Engineering Application Support in a large distributed Unix and Windows server environment. Strong problem determination skills. Good knowledge of networking, remote diagnostic techniques, firewalls and network security. Extensive experience with engineering application and database servers, high-availability systems, high-performance computing clusters, and process automation.