Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

So, say, I have a directory with a bunch of files like g.txt, where g.txt was last modified on, say, June 20, 2012.

How would I batch-rename all of the files (like g.txt) with the last modified date of June 20, 2012 appended on the end?

share|improve this question
add comment

4 Answers

up vote 8 down vote accepted

Quick-and-dirty Bash one-liner to rename all (globbed) files in the current directory from filename.txt to filename.txt-20120620:

for f in *; do mv -- "$f" "$f-$(stat -c %Y "$f" | date +%Y%m%d)"; done

An enterprising Bash nerd will find some edge case to break it, I'm sure. :)

Obviously, this doesn't do desirable things like checking whether a file already has something that looks like a date at the end.

share|improve this answer
2  
+1, looks good. you can make it a little more robust by using '--' as the first arg to mv. guards against filenaes begining with '-' –  cas Jul 18 '12 at 3:28
3  
What kind of date implementation is that? My GNU date doesn't seem to handle input. –  manatwork Jul 19 '12 at 7:33
    
coreutils 8.4 on RHEL 6. –  jgoldschrafe Jul 19 '12 at 12:37
1  
@jgoldschrafe, are you sure that code properly works for you? pastebin.com/nNYubf3A –  manatwork Jul 19 '12 at 15:00
5  
good catch manatwork. my GNU date 8.13 doesn't support reading from stdin either. It does support using the mod time of a file with "-r" - e.g. "date +%Y%m%d -r $f", so stat isn't even needed in this particular case. –  cas Jul 20 '12 at 0:45
show 2 more comments

here's a version of goldschrafe's one-liner that:

  • doesn't use stat
  • works with earlier versions of GNU date
  • correctly copes with any spaces in the filenames
  • also copes with filenames beginning with a dash

    for f in *; do mv -- "$f" "$f-$(date +%Y%m%d -r "${f}")"; done

share|improve this answer
add comment

As I understood we don't know beforehand what is the modification date. So we need to get it from each file, format the output and rename each file in a way so that it includes the modification date in the filenames.

You can save this script as something like "modif_date.sh" and make it executable. We invoke it with the target directory as the argument:

modif_date.sh txt_collection

Where "txt_collection" is the name of the directory where we have all the files that we want to rename.

#!/bin/sh

# Override any locale setting to get known month names
export LC_ALL=c
# First we check for the argument
if [ -z "$1" ]; then
    echo "Usage: $0 directory"
    exit 1
fi

# Here we check if the argument is an absolute or relative path. It works both ways
case "${1}" in
  /*) work_dir=${1};;
  *) work_dir=${PWD}/${1};;
esac

# We need a for loop to treat file by file inside our target directory
for i in *; do
    # If the modification date is in the same year, "ls -l" shows us the timestamp.
    # So in this case we use our current year. 
    test_year=`ls -Ggl "${work_dir}/${i}" | awk '{ print $6 }'`
    case ${test_year} in *:*) 
        modif_year=`date '+%Y'`
        ;;
    *)
        modif_year=${test_year}
        ;;
    esac
    # The month output from "ls -l" is in short names. We convert it to numbers.
    name_month=`ls -Ggl "${work_dir}/${i}" | awk '{ print $4 }'`
    case ${name_month} in
            Jan) num_month=01 ;;
            Feb) num_month=02 ;;
        Mar) num_month=03 ;;
        Apr) num_month=04 ;;
        May) num_month=05 ;;
        Jun) num_month=06 ;;
        Jul) num_month=07 ;;
        Aug) num_month=08 ;;
        Sep) num_month=09 ;;
        Oct) num_month=10 ;;
        Nov) num_month=11 ;;
        Dec) num_month=12 ;;
        *) echo "ERROR!"; exit 1 ;;
    esac
    # Here is the date we will use for each file
    modif_date=`ls -Ggl "${work_dir}/${i}" | awk '{ print $5 }'`${num_month}${modif_year}
    # And finally, here we actually rename each file to include
    # the last modification date as part of the filename.
    mv "${work_dir}/${i}" "${work_dir}/${i}-${modif_date}"
done
share|improve this answer
1  
read the man page for stat(1) to find out how you can shrink your script by about 3/4...most of the above is re-inventing the wheel. in particular, 'stat -c %Y' will give you the modification time of a file in seconds since the epoch (1970-01-01 00:00:00). This can then be used as input to date(1) to format the timestamp as required. see jgoldschrafe's answer above for an example. –  cas Jul 18 '12 at 5:56
    
@CraigSanders The upside is that this script works under operating systems other than Linux, which have a different stat utility or none at all. –  Gilles Jul 19 '12 at 0:42
    
Note that parsing the output of ls is not reliable. On some unix variants, user and group names can contain spaces, which will throw off the alignment of the date columns. –  Gilles Jul 19 '12 at 0:48
    
@giles: true, but anyone sensible :-) would install GNU coreutils (and all of the other GNU tools) on any non-linux system. It's the easiest way to get not only a good set of userland tools, but also a consistent set regardless of the underlying kernel. –  cas Jul 19 '12 at 4:54
add comment

Obligatory zsh one-liner (not counting the one-time loading of optional components):

zmodload zsh/stat
autoload -U zmv
zmv -n '(*)' '$1-$(stat -F %Y%m%d +mtime -- $1)'

We use the stat builtin from the zsh/stat module, and the zmv function to rename files. And here's an extra which places the date before the extension, if any.

zmv -n '(*)' '$1:r-$(stat -F %Y%m%d +mtime -- $1)${${1:e}:+.$1:e}'
share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.