Bash – Rename multiple files, add date in the middle

bashfindmvshell-scriptsolaris

I want to rename all files in a folder, including a current time stamp in the name, retaining the original extension, in Solaris, like this:

test1.txt > test1.date.txt

I tried this, but I lose the first part of the name:

find * -prune -type f -name '*.txt' -exec mv {} {}.$(date +'%Y%m%d%H%M').txt \;

What am I missing ?

Best Answer

find . -type f -name '*.txt' -exec sh -c '
    now=$( date +%Y%m%d%H%M )
    for pathname do
        mv "$pathname" "${pathname%.txt}.$now.txt"
    done' sh {} +

This would find all pathnames of regular files in or below the current directory, whose names end in .txt. For batches of these, a short shell script is called. This script will first get the timestamp (this is had only once for each batch, for efficiency) and then loop over the given pathnames.

For each pathname, it will rename it by removing the final .txt (using ${pathname%.txt}), and adding a dot, the timestamp and .txt.

This has not been tested on Solaris, but uses only standard components and options etc.

To compute the timestamp only once, before calling find, use

now=$( date +%Y%m%d%H%M ) \
find ...as before (but without assigning to now)...

or

env now="$( date +%Y%m%d%H%M )" \
find ...as before (but without assigning to now)...

(note the line continuations in the above two commands)

If the files are located in a single directory, just run the loop:

now=$( date +%Y%m%d%H%M )
for pathname in ./*.txt; do
    mv "$pathname" "${pathname%.txt}.$now.txt"
done

This assumes that the pattern ./*.txt matches all names that you'd like to rename.

Related:


Your {}.$(date +'%Y%m%d%H%M').txt does not work portably. It may work with some implementations of find, but an implementation is not required to expand {} to the current pathname if the {} occurs together with another string.

The relevant text from the POSIX standard is:

A utility_name or argument containing only the two characters {} shall be replaced by the current pathname. If a utility_name or argument string contains the two characters {}, but not just the two characters {}, it is implementation-defined whether find replaces those two characters or uses the string without change.

Related Question