Bulk rename files in Unix with current date as suffix Bulk rename files in Unix with current date as suffix unix unix

Bulk rename files in Unix with current date as suffix


You want $1 to be passed literally to rename, yet have $(date +%F) to be expanded by the shell. The latter won't happen when you use single quotes, only with double quotes. The solution is to use double quotes and escape $1 so the shell doesn't expand it.

rename "s/(.*)/\$1_$(date +%F)/" *


Portable Posix shell solution

Since you said "in Unix" and the rename command isn't portable (it's actually a part of the perl package), here is a solution that should work in more environments:

for file in *; do mv "$file" "${file}_$( date +%F )"; done

This creates a loop and then moves each individual file to the new name. Like the question, it uses date +%F via shell process substitution to insert the "full date" (YYYY-mm-dd). Process substitution must use double quotes (") and not single quotes (') because single quotes shut off shell interpretations (this goes for variables as well), as noted in John's answer.

Argument list too long

Your shell will complain if the directory has too many files in it, with an error like "Argument list too long." This is because * expands to the contents of the directory, all of which become arguments to the command you're running.

To solve that, create a script and then feed it through xargs as follows:

#!/bin/shif [ -z "$1" ]; then set *; fi  # default to all files in curent directoryfor file in "$@"; do mv "$file" "${file}_$( date +%F )"; done

Using ls to generate a file list isn't always wise for scripts (it can do weird things in certain contexts). Use find instead:

find . -type f -not -name '*_20??-??-??' -print0 |xargs -0 sh /path/to/script.sh

Note, that command is recursive (change that by adding -maxdepth 1 after the dot). find is extremely capable. In this example, it finds all files (-type f) that do not match the shell glob *_20??-??-?? (* matches any number of any characters, ? matches exactly one of any character, so this matches abc_2016-10-14 but not abc_2016-10-14-def). This uses find … -print0 and xargs -0 to ensure spacing is properly preserved (instead of spaces, these use the null character as the delimiter).

You can also do this with perl rename:

find . -type f -not -name '*_20??-??-??' -print0 |xargs -0 \  rename "s/(.*)/\$1_$( date +%F )/"